• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 #ifndef TENSORFLOW_C_EXPERIMENTAL_FILESYSTEM_FILESYSTEM_INTERFACE_H_
16 #define TENSORFLOW_C_EXPERIMENTAL_FILESYSTEM_FILESYSTEM_INTERFACE_H_
17 
18 #include <stddef.h>
19 #include <stdint.h>
20 
21 #include "tensorflow/c/tf_file_statistics.h"
22 #include "tensorflow/c/tf_status.h"
23 
24 /// This is the interop header between core TensorFlow and modular filesystem
25 /// plugins (see initial RFC https://github.com/tensorflow/community/pull/101).
26 ///
27 /// Both core TensorFlow and every plugin will use this header. The associated
28 /// `.cc` file is only used by core TensorFlow to implement checking needed for
29 /// plugin registration and ensuring API and ABI compatibility. Plugin authors
30 /// don't need to read the `.cc` file but they should consult every section of
31 /// this file to ensure a compliant plugin can be built and that the plugin can
32 /// be used without recompilation in the widest range of TensorFlow versions.
33 ///
34 /// The header is divided into sections, as follows:
35 ///   1. Opaque plugin private data structures and wrappers for type safety;
36 ///   2. Function tables for plugin functionality;
37 ///   3. Versioning metadata;
38 ///   4. Plugin registration API and the DSO entry point.
39 
40 #ifdef __cplusplus
41 extern "C" {
42 #endif  // __cplusplus
43 
44 /// SECTION 1. Opaque data structures to hold plugin specific data
45 /// ----------------------------------------------------------------------------
46 ///
47 /// The following data structures incorporate a `void*` that is opaque to
48 /// TensorFlow but can be used by each filesystem plugin to represent internal
49 /// data.
50 ///
51 /// We prefer to have these structures instead of passing `void*` into
52 /// method signatures to have some type of type safety: for example, operations
53 /// that are only valid on random access files have a `TF_RandomAccessFile`
54 /// argument.
55 ///
56 /// Lifetime: The wrapper data structures are owned by core TensorFlow. The data
57 /// pointed to by the `void*` members is always owned by the plugin. The plugin
58 /// will provide functions to call to allocate and deallocate this data (see
59 /// next sections) and core TensorFlow ensures to call these at the proper time.
60 ///
61 /// Plugins will never receive a `TF_*` pointer that is `nullptr`. Core
62 /// TensorFlow will never touch the `void*` wrapped by these structures, except
63 /// to initialize it as `nullptr`.
64 
65 typedef struct TF_RandomAccessFile {
66   void* plugin_file;
67 } TF_RandomAccessFile;
68 
69 typedef struct TF_WritableFile {
70   void* plugin_file;
71 } TF_WritableFile;
72 
73 typedef struct TF_ReadOnlyMemoryRegion {
74   void* plugin_memory_region;
75 } TF_ReadOnlyMemoryRegion;
76 
77 typedef struct TF_Filesystem {
78   void* plugin_filesystem;
79 } TF_Filesystem;
80 
81 typedef struct TF_TransactionToken {
82   void* token;
83   TF_Filesystem* owner;
84 } TF_TransactionToken;
85 
86 typedef struct TF_Filesystem_Option_Value {
87   int type_tag;
88   int num_values;
89   union {
90     int64_t inv_val;
91     double real_val;
92     struct {
93       char* buf;
94       int buf_length;
95     } buffer_val;
96   } * values;  // owned
97 } TF_Filesystem_Option_Value;
98 
99 typedef struct TF_Filesystem_Option {
100   char* name;                         // null terminated, owned
101   char* description;                  // null terminated, owned
102   int per_file;                       // bool actually, but bool is not a C type
103   TF_Filesystem_Option_Value* value;  // owned
104 } TF_Filesystem_Option;
105 
106 /// SECTION 2. Function tables for functionality provided by plugins
107 /// ----------------------------------------------------------------------------
108 ///
109 /// The following data structures represent the function tables for operations
110 /// that plugins provide (some are mandatory, some are optional, with or without
111 /// a default implementation).
112 ///
113 /// Each plugin implements the operations that are supported and TensorFlow will
114 /// properly handle the cases when an operation is not supported (i.e., return
115 /// the corresponding `Status` value).
116 ///
117 /// REQUIRED OPERATIONS: All required operations are marked as such, including
118 /// operations which are conditionally required. If the presence of an operation
119 /// `foo` requires operation `bar` to be present, this is specified in `foo`. If
120 /// the entire set of operations in a table is not provided, use `nullptr` for
121 /// the struct pointer (e.g., when a file type is not supported).
122 ///
123 /// DEFAULT IMPLEMENTATIONS: Some operations have default implementations that
124 /// TensorFlow uses in case the plugin doesn't supply its own version. An
125 /// operation `foo` might have a default implementation which uses `bar` and
126 /// `foobar`. If the plugin supplies `bar` and `foobar`, TensorFlow can use the
127 /// default implementation of `foo`.
128 ///
129 /// During plugin loading, plugins will call the registration function provided
130 /// by this interface, supplying values for each of these structures. Core
131 /// TensorFlow checks that the plugin supplies all mandatory operations and
132 /// then copies these tables to a different memory location, marking the new
133 /// operation tables as read-only. Once a plugin is loaded, none of these
134 /// operation pointers may change.
135 ///
136 /// There are 4 function tables: one for each of the 3 file objects in
137 /// TensorFlow (i.e., `RandomAccessFile`, `WritableFile`,
138 /// `ReadOnlyMemoryRegion`) and one for all the operations a `Filesystem`
139 /// implements. Each of them is in a 1-to-1 correspondence with the wrapper
140 /// structures from the first section: these tables only contain function
141 /// pointers that operate on the corresponding data. Thus, the first argument of
142 /// each of these functions is a pointer to the paired struct and this argument
143 /// can be used to track state in between calls (from an object oriented point
144 /// of view, this can be viewed as a "vtable" for a "class" -- that is the
145 /// corresponding struct above --; the first argument is in place of `this`).
146 ///
147 /// Except where noted otherwise, all pointer arguments are owned by core
148 /// TensorFlow and are guaranteed to not be `nullptr`.
149 ///
150 /// All path-like arguments are null terminated `char*` strings. Plugins can
151 /// assume that before any function using path arguments is invoked, the path is
152 /// made canonical by calling the function provided by `translate_name` or a
153 /// default implementation of that (supplied by core TensorFlow).
154 ///
155 /// The only time the pointer to the `TF_*` structures from section 1 is not
156 /// marked `const` in these functions is when these function are either
157 /// allocating or deallocating the plugin specific data. That is, in the 4
158 /// `cleanup` functions (one for each data structure), the `init` function for
159 /// `TF_Filesystem` and the `new_*` methods of `TF_FilesystemOps` to initialize
160 /// the 3 types of files. In all other cases, there is no need to modify the
161 /// address of the opaque data pointer, hence the wrapper pointer is marked
162 /// `const`.
163 ///
164 /// For consistency, the arguments on all these functions follow the same
165 /// pattern: first we have the opaque pointer argument ("this" above), then the
166 /// input arguments, then the in-out arguments (if any) and we finish the
167 /// argument list with the out arguments. We only use the return type for an out
168 /// parameter if that is a plain C type, as this ensures ABI compatibility
169 /// (returning structures has issues in case compiler options affect
170 /// optimizations such as RVO). If a status needs to be returned from these
171 /// methods, the last argument is always a `TF_Status *` (or an array of such
172 /// pointers) owned by core TensorFlow and guaranteed to not be `nullptr`.
173 ///
174 /// To ensure ABI and API compatibility, we have out-of-bounds data that is used
175 /// by both core TensorFlow and the plugin at load time. We don't include this
176 /// data in the structures here to prevent cases when padding/packing enabled by
177 /// different compiler options breaks compatibility. For more details about how
178 /// this is used, please consult next sections. Here we just wrap these tables
179 /// in lint warnings so that changes here cause changes to the versioning data
180 /// as well. Here is a short summary of what changes are allowed:
181 ///   * adding a new method at the end of a table is allowed at any time;
182 ///   * any other change to these tables is only allowed on a major TensorFlow
183 ///     version change (e.g., from 2.x to 3.0). This is provided as an escape
184 ///     hatch to allow cleaning up these tables. Since any of these changes
185 ///     break ABI compatibility and cause all plugins to be recompiled, these
186 ///     type of changes should be extremely rare.
187 ///
188 /// Next section will detail this as well as some corner cases that are out of
189 /// scope for now.
190 
191 // LINT.IfChange
192 typedef struct TF_RandomAccessFileOps {
193   /// Releases resources associated with `*file`.
194   ///
195   /// Requires that `*file` is not used in any concurrent or subsequent
196   /// operations.
197   ///
198   /// This operation must be provided. See "REQUIRED OPERATIONS" above.
199   void (*cleanup)(TF_RandomAccessFile* file);
200 
201   /// Reads up to `n` bytes from `*file` starting at `offset`.
202   ///
203   /// The output is in `buffer`, core TensorFlow owns the buffer and guarantees
204   /// that at least `n` bytes are available.
205   ///
206   /// Returns number of bytes read or -1 in case of error. Because of this
207   /// constraint and the fact that `ssize_t` is not defined in `stdint.h`/C++
208   /// standard, the return type is `int64_t`.
209   ///
210   /// This is thread safe.
211   ///
212   /// Note: the `buffer` argument is NOT a null terminated string!
213   ///
214   /// Plugins:
215   ///   * Must set `status` to `TF_OK` if exactly `n` bytes have been read.
216   ///   * Must set `status` to `TF_OUT_OF_RANGE` if fewer than `n` bytes have
217   ///     been read due to EOF.
218   ///   * Must return -1 for any other error and must set `status` to any
219   ///     other value to provide more information about the error.
220   int64_t (*read)(const TF_RandomAccessFile* file, uint64_t offset, size_t n,
221                   char* buffer, TF_Status* status);
222 } TF_RandomAccessFileOps;
223 // LINT.ThenChange(:random_access_file_ops_version)
224 
225 // LINT.IfChange
226 typedef struct TF_WritableFileOps {
227   /// Releases resources associated with `*file`.
228   ///
229   /// Requires that `*file` is not used in any concurrent or subsequent
230   /// operations.
231   ///
232   /// This operation must be provided. See "REQUIRED OPERATIONS" above.
233   void (*cleanup)(TF_WritableFile* file);
234 
235   /// Appends `buffer` of size `n` to `*file`.
236   ///
237   /// Core TensorFlow owns `buffer` and guarantees at least `n` bytes of storage
238   /// that can be used to write data.
239   ///
240   /// Note: the `buffer` argument is NOT a null terminated string!
241   ///
242   /// Plugins:
243   ///   * Must set `status` to `TF_OK` if exactly `n` bytes have been written.
244   ///   * Must set `status` to `TF_RESOURCE_EXHAUSTED` if fewer than `n` bytes
245   ///     have been written, potentially due to quota/disk space.
246   ///   * Might use any other error value for `status` to signal other errors.
247   void (*append)(const TF_WritableFile* file, const char* buffer, size_t n,
248                  TF_Status* status);
249 
250   /// Returns the current write position in `*file`.
251   ///
252   /// Plugins should ensure that the implementation is idempotent, 2 identical
253   /// calls result in the same answer.
254   ///
255   /// Plugins:
256   ///   * Must set `status` to `TF_OK` and return current position if no error.
257   ///   * Must set `status` to any other value and return -1 in case of error.
258   int64_t (*tell)(const TF_WritableFile* file, TF_Status* status);
259 
260   /// Flushes `*file` and syncs contents to filesystem.
261   ///
262   /// This call might not block, and when it returns the contents might not have
263   /// been fully persisted.
264   ///
265   /// DEFAULT IMPLEMENTATION: No op.
266   void (*flush)(const TF_WritableFile* file, TF_Status* status);
267 
268   /// Syncs contents of `*file` with the filesystem.
269   ///
270   /// This call should block until filesystem confirms that all buffers have
271   /// been flushed and persisted.
272   ///
273   /// DEFAULT IMPLEMENTATION: No op.
274   void (*sync)(const TF_WritableFile* file, TF_Status* status);
275 
276   /// Closes `*file`.
277   ///
278   /// Flushes all buffers and deallocates all resources.
279   ///
280   /// Calling `close` must not result in calling `cleanup`.
281   ///
282   /// Core TensorFlow will never call `close` twice.
283   void (*close)(const TF_WritableFile* file, TF_Status* status);
284 } TF_WritableFileOps;
285 // LINT.ThenChange(:writable_file_ops_version)
286 
287 // LINT.IfChange
288 typedef struct TF_ReadOnlyMemoryRegionOps {
289   /// Releases resources associated with `*region`.
290   ///
291   /// Requires that `*region` is not used in any concurrent or subsequent
292   /// operations.
293   ///
294   /// This operation must be provided. See "REQUIRED OPERATIONS" above.
295   void (*cleanup)(TF_ReadOnlyMemoryRegion* region);
296 
297   /// Returns a pointer to the memory region.
298   ///
299   /// This operation must be provided. See "REQUIRED OPERATIONS" above.
300   const void* (*data)(const TF_ReadOnlyMemoryRegion* region);
301 
302   /// Returns the length of the memory region in bytes.
303   ///
304   /// This operation must be provided. See "REQUIRED OPERATIONS" above.
305   uint64_t (*length)(const TF_ReadOnlyMemoryRegion* region);
306 } TF_ReadOnlyMemoryRegionOps;
307 // LINT.ThenChange(:read_only_memory_region_ops_version)
308 
309 // LINT.IfChange
310 typedef struct TF_FilesystemOps {
311   /// Acquires all resources used by the filesystem.
312   ///
313   /// This operation must be provided. See "REQUIRED OPERATIONS" above.
314   void (*init)(TF_Filesystem* filesystem, TF_Status* status);
315 
316   /// Releases all resources used by the filesystem
317   ///
318   /// NOTE: TensorFlow does not unload DSOs. Thus, the only way a filesystem
319   /// won't be registered anymore is if this function gets called by core
320   /// TensorFlow and the `TF_Filesystem*` object is destroyed. However, due to
321   /// registration being done in a static instance of `Env`, the destructor of
322   /// `FileSystem` is never called (see
323   /// https://github.com/tensorflow/tensorflow/issues/27535). In turn, this
324   /// function will never be called. There are plans to refactor registration
325   /// and fix this.
326   ///
327   /// TODO(mihaimaruseac): After all filesystems are converted, revisit note.
328   ///
329   /// This operation must be provided. See "REQUIRED OPERATIONS" above.
330   void (*cleanup)(TF_Filesystem* filesystem);
331 
332   /// Creates a new random access read-only file from given `path`.
333   ///
334   /// After this call `file` may be concurrently accessed by multiple threads.
335   ///
336   /// Plugins:
337   ///   * Must set `status` to `TF_OK` if `file` was updated.
338   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't point to an
339   ///     existing file or one of the parent entries in `path` doesn't exist.
340   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` points to a
341   ///     directory or if it is invalid (e.g., malformed, or has a parent entry
342   ///     which is a file).
343   ///   * Might use any other error value for `status` to signal other errors.
344   ///
345   /// REQUIREMENTS: If plugins implement this, they must also provide a filled
346   /// `TF_RandomAccessFileOps` table. See "REQUIRED OPERATIONS" above.
347   void (*new_random_access_file)(const TF_Filesystem* filesystem,
348                                  const char* path, TF_RandomAccessFile* file,
349                                  TF_Status* status);
350 
351   /// Creates an object to write to a file with the specified `path`.
352   ///
353   /// If the file already exists, it is deleted and recreated. The `file` object
354   /// must only be accessed by one thread at a time.
355   ///
356   /// Plugins:
357   ///   * Must set `status` to `TF_OK` if `file` was updated.
358   ///   * Must set `status` to `TF_NOT_FOUND` if one of the parents entries in
359   ///     `path` doesn't exist.
360   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` points to a
361   ///     directory or if it is invalid.
362   ///   * Might use any other error value for `status` to signal other errors.
363   ///
364   /// REQUIREMENTS: If plugins implement this, they must also provide a filled
365   /// `TF_WritableFileOps` table. See "REQUIRED OPERATIONS" above.
366   void (*new_writable_file)(const TF_Filesystem* filesystem, const char* path,
367                             TF_WritableFile* file, TF_Status* status);
368 
369   /// Creates an object to append to a file with the specified `path`.
370   ///
371   /// If the file doesn't exists, it is first created with empty contents.
372   /// The `file` object must only be accessed by one thread at a time.
373   ///
374   /// Plugins:
375   ///   * Must set `status` to `TF_OK` if `file` was updated.
376   ///   * Must set `status` to `TF_NOT_FOUND` if one of the parents entries in
377   ///     `path` doesn't exist.
378   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` points to a
379   ///     directory or if it is invalid.
380   ///   * Might use any other error value for `status` to signal other errors.
381   ///
382   /// REQUIREMENTS: If plugins implement this, they must also provide a filled
383   /// `TF_WritableFileOps` table. See "REQUIRED OPERATIONS" above.
384   void (*new_appendable_file)(const TF_Filesystem* filesystem, const char* path,
385                               TF_WritableFile* file, TF_Status* status);
386 
387   /// Creates a read-only region of memory from contents of `path`.
388   ///
389   /// After this call `region` may be concurrently accessed by multiple threads.
390   ///
391   /// Plugins:
392   ///   * Must set `status` to `TF_OK` if `region` was updated.
393   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't point to an
394   ///     existing file or one of the parent entries in `path` doesn't exist.
395   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` points to a
396   ///     directory or if it is invalid.
397   ///   * Must set `status` to `TF_INVALID_ARGUMENT` if `path` points to an
398   ///     empty file.
399   ///   * Might use any other error value for `status` to signal other errors.
400   ///
401   /// REQUIREMENTS: If plugins implement this, they must also provide a filled
402   /// `TF_ReadOnlyMemoryRegionOps` table. See "REQUIRED OPERATIONS" above.
403   void (*new_read_only_memory_region_from_file)(const TF_Filesystem* filesystem,
404                                                 const char* path,
405                                                 TF_ReadOnlyMemoryRegion* region,
406                                                 TF_Status* status);
407 
408   /// Creates the directory specified by `path`, assuming parent exists.
409   ///
410   /// Plugins:
411   ///   * Must set `status` to `TF_OK` if directory was created.
412   ///   * Must set `status` to `TF_NOT_FOUND` if one of the parents entries in
413   ///     `path` doesn't exist.
414   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` is invalid.
415   ///   * Must set `status` to `TF_ALREADY_EXISTS` if `path` already exists.
416   ///   * Might use any other error value for `status` to signal other errors.
417   void (*create_dir)(const TF_Filesystem* filesystem, const char* path,
418                      TF_Status* status);
419 
420   /// Creates the directory specified by `path` and all needed ancestors.
421   ///
422   /// Plugins:
423   ///   * Must set `status` to `TF_OK` if directory was created.
424   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` is invalid or
425   ///     if it exists but is not a directory.
426   ///   * Might use any other error value for `status` to signal other errors.
427   ///
428   /// NOTE: The requirements specify that `TF_ALREADY_EXISTS` is not returned if
429   /// directory exists. Similarly, `TF_NOT_FOUND` is not be returned, as the
430   /// missing directory entry and all its descendants will be created by the
431   /// plugin.
432   ///
433   /// DEFAULT IMPLEMENTATION: Creates directories one by one. Needs
434   /// `path_exists`, `is_directory`, and `create_dir`.
435   void (*recursively_create_dir)(const TF_Filesystem* filesystem,
436                                  const char* path, TF_Status* status);
437 
438   /// Deletes the file specified by `path`.
439   ///
440   /// Plugins:
441   ///   * Must set `status` to `TF_OK` if file was deleted.
442   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't exist.
443   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` points to a
444   ///     directory or if it is invalid.
445   ///   * Might use any other error value for `status` to signal other errors.
446   void (*delete_file)(const TF_Filesystem* filesystem, const char* path,
447                       TF_Status* status);
448 
449   /// Deletes the empty directory specified by `path`.
450   ///
451   /// Plugins:
452   ///   * Must set `status` to `TF_OK` if directory was deleted.
453   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't exist.
454   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` does not point
455   ///     to a directory, if `path` is invalid, or if directory is not empty.
456   ///   * Might use any other error value for `status` to signal other errors.
457   void (*delete_dir)(const TF_Filesystem* filesystem, const char* path,
458                      TF_Status* status);
459 
460   /// Deletes the directory specified by `path` and all its contents.
461   ///
462   /// This is accomplished by traversing directory tree rooted at `path` and
463   /// deleting entries as they are encountered, from leaves to root. Each plugin
464   /// is free to choose a different approach which obtains similar results.
465   ///
466   /// On successful deletion, `status` must be `TF_OK` and `*undeleted_files`
467   /// and `*undeleted_dirs` must be 0. On unsuccessful deletion, `status` must
468   /// be set to the reason why one entry couldn't be removed and the proper
469   /// count must be updated. If the deletion is unsuccessful because the
470   /// traversal couldn't start, `*undeleted_files` must be set to 0 and
471   /// `*undeleted_dirs` must be set to 1.
472   ///
473   /// TODO(mihaimaruseac): After all filesystems are converted, consider
474   /// invariant about `*undeleted_files` and `*undeleted_dirs`.
475   ///
476   /// Plugins:
477   ///   * Must set `status` to `TF_OK` if directory was deleted.
478   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't exist.
479   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` is invalid.
480   ///   * Might use any other error value for `status` to signal other errors.
481   ///
482   /// DEFAULT IMPLEMENTATION: Does a BFS traversal of tree rooted at `path`,
483   /// deleting entries as needed. Needs `path_exists`, `get_children`,
484   /// `is_directory`, `delete_file`, and `delete_dir`.
485   void (*delete_recursively)(const TF_Filesystem* filesystem, const char* path,
486                              uint64_t* undeleted_files,
487                              uint64_t* undeleted_dirs, TF_Status* status);
488 
489   /// Renames the file given by `src` to that in `dst`.
490   ///
491   /// Replaces `dst` if it exists. In case of error, both `src` and `dst` keep
492   /// the same state as before the call.
493   ///
494   /// Plugins:
495   ///   * Must set `status` to `TF_OK` if rename was completed.
496   ///   * Must set `status` to `TF_NOT_FOUND` if one of the parents entries in
497   ///     either `src` or `dst` doesn't exist or if the specified `src` path
498   ///     doesn't exist.
499   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if either `src` or
500   ///     `dst` is a directory or if either of them is invalid.
501   ///   * Might use any other error value for `status` to signal other errors.
502   ///
503   /// DEFAULT IMPLEMENTATION: Copies file and deletes original. Needs
504   /// `copy_file`. and `delete_file`.
505   void (*rename_file)(const TF_Filesystem* filesystem, const char* src,
506                       const char* dst, TF_Status* status);
507 
508   /// Copies the file given by `src` to that in `dst`.
509   ///
510   /// Similar to `rename_file`, but both `src` and `dst` exist after this call
511   /// with the same contents. In case of error, both `src` and `dst` keep the
512   /// same state as before the call.
513   ///
514   /// If `dst` is a directory, creates a file with the same name as the source
515   /// inside the target directory.
516   ///
517   /// Plugins:
518   ///   * Must set `status` to `TF_OK` if rename was completed.
519   ///   * Must set `status` to `TF_NOT_FOUND` if one of the parents entries in
520   ///     either `src` or `dst` doesn't exist or if the specified `src` path
521   ///     doesn't exist.
522   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if either `src` or
523   ///     `dst` is a directory or if either of them is invalid.
524   ///   * Might use any other error value for `status` to signal other errors.
525   ///
526   /// DEFAULT IMPLEMENTATION: Reads from `src` and writes to `dst`. Needs
527   /// `new_random_access_file` and `new_writable_file`.
528   void (*copy_file)(const TF_Filesystem* filesystem, const char* src,
529                     const char* dst, TF_Status* status);
530 
531   /// Checks if `path` exists.
532   ///
533   /// Note that this doesn't differentiate between files and directories.
534   ///
535   /// Plugins:
536   ///   * Must set `status` to `TF_OK` if `path` exists.
537   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't point to a
538   ///     filesystem entry.
539   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` is invalid.
540   ///   * Might use any other error value for `status` to signal other errors.
541   void (*path_exists)(const TF_Filesystem* filesystem, const char* path,
542                       TF_Status* status);
543 
544   /// Checks if all values in `paths` exist in the filesystem.
545   ///
546   /// Returns `true` if and only if calling `path_exists` on each entry in
547   /// `paths` would set `status` to `TF_OK`.
548   ///
549   /// Caller guarantees that:
550   ///   * `paths` has exactly `num_files` entries.
551   ///   * `statuses` is either null or an array of `num_files` non-null elements
552   ///     of type `TF_Status*`.
553   ///
554   /// If `statuses` is not null, plugins must fill each element with detailed
555   /// status for each file, as if calling `path_exists` on each one. Core
556   /// TensorFlow initializes the `statuses` array and plugins must use
557   /// `TF_SetStatus` to set each element instead of directly assigning.
558   ///
559   /// DEFAULT IMPLEMENTATION: Checks existence of every file. Needs
560   /// `path_exists`.
561   bool (*paths_exist)(const TF_Filesystem* filesystem, char** paths,
562                       int num_files, TF_Status** statuses);
563 
564   /// Obtains statistics for the given `path`.
565   ///
566   /// Updates `stats` only if `status` is set to `TF_OK`.
567   ///
568   /// Plugins:
569   ///   * Must set `status` to `TF_OK` if `path` exists.
570   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't point to a
571   ///     filesystem entry.
572   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` is invalid.
573   ///   * Might use any other error value for `status` to signal other errors.
574   void (*stat)(const TF_Filesystem* filesystem, const char* path,
575                TF_FileStatistics* stats, TF_Status* status);
576 
577   /// Checks whether the given `path` is a directory or not.
578   ///
579   /// If `status` is not `TF_OK`, returns `false`, otherwise returns the same
580   /// as the `is_directory` member of a `TF_FileStatistics` that would be used
581   /// on the equivalent call of `stat`.
582   ///
583   /// Plugins:
584   ///   * Must set `status` to `TF_OK` if `path` exists.
585   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't point to a
586   ///     filesystem entry.
587   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` is invalid.
588   ///   * Might use any other error value for `status` to signal other errors.
589   ///
590   /// DEFAULT IMPLEMENTATION: Gets statistics about `path`. Needs `stat`.
591   bool (*is_directory)(const TF_Filesystem* filesystem, const char* path,
592                        TF_Status* status);
593 
594   /// Returns the size of the file given by `path`.
595   ///
596   /// If `status` is not `TF_OK`, return value is undefined. Otherwise, returns
597   /// the same as `length` member of a `TF_FileStatistics` that would be used on
598   /// the equivalent call of `stat`.
599   ///
600   /// Plugins:
601   ///   * Must set `status` to `TF_OK` if `path` exists.
602   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't point to a
603   ///     filesystem entry.
604   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path` is invalid or
605   ///     points to a directory.
606   ///   * Might use any other error value for `status` to signal other errors.
607   ///
608   /// DEFAULT IMPLEMENTATION: Gets statistics about `path`. Needs `stat`.
609   int64_t (*get_file_size)(const TF_Filesystem* filesystem, const char* path,
610                            TF_Status* status);
611 
612   /// Translates `uri` to a filename for the filesystem
613   ///
614   /// A filesystem is registered for a specific scheme and all of the methods
615   /// should work with URIs. Hence, each filesystem needs to be able to
616   /// translate from an URI to a path on the filesystem. For example, this
617   /// function could translate `fs:///path/to/a/file` into `/path/to/a/file`, if
618   /// implemented by a filesystem registered to handle the `fs://` scheme.
619   ///
620   /// A new `char*` buffer must be allocated by this method. Core TensorFlow
621   /// manages the lifetime of the buffer after the call. Thus, all callers of
622   /// this method must take ownership of the returned pointer.
623   ///
624   /// The implementation should clean up paths, including but not limited to,
625   /// removing duplicate `/`s, and resolving `..` and `.`.
626   ///
627   /// Plugins must not return `nullptr`. Returning empty strings is allowed.
628   ///
629   /// The allocation and freeing of memory must happen via the functions sent to
630   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
631   /// structure in Section 4).
632   ///
633   /// This function will be called by core TensorFlow to clean up all path
634   /// arguments for all other methods in the filesystem API.
635   ///
636   /// DEFAULT IMPLEMENTATION: Uses `io::CleanPath` and `io::ParseURI`.
637   char* (*translate_name)(const TF_Filesystem* filesystem, const char* uri);
638 
639   /// Finds all entries in the directory given by `path`.
640   ///
641   /// The returned entries are paths relative to `path`.
642   ///
643   /// Plugins must allocate `entries` to hold all names that need to be returned
644   /// and return the size of `entries`. Caller takes ownership of `entries`
645   /// after the call.
646   ///
647   /// In case of error, plugins must set `status` to a value different than
648   /// `TF_OK`, free memory allocated for `entries` and return -1.
649   ///
650   /// The allocation and freeing of memory must happen via the functions sent to
651   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
652   /// structure in Section 4).
653   ///
654   /// Plugins:
655   ///   * Must set `status` to `TF_OK` if all children were returned.
656   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't point to a
657   ///     filesystem entry or if one of the parents entries in `path` doesn't
658   ///     exist.
659   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if one of the parent
660   ///     entries in `path` is not a directory, or if `path` is a file.
661   ///   * Might use any other error value for `status` to signal other errors.
662   int (*get_children)(const TF_Filesystem* filesystem, const char* path,
663                       char*** entries, TF_Status* status);
664 
665   /// Finds all entries matching the regular expression given by `glob`.
666   ///
667   /// Pattern must match the entire entry name, not just a substring.
668   ///
669   /// pattern: { term }
670   /// term:
671   ///   '*': matches any sequence of non-'/' characters
672   ///   '?': matches a single non-'/' character
673   ///   '[' [ '^' ] { match-list } ']':
674   ///        matches any single character (not) on the list
675   ///   c: matches character c (c != '*', '?', '\\', '[')
676   ///   '\\' c: matches character c
677   /// character-range:
678   ///   c: matches character c (c != '\\', '-', ']')
679   ///   '\\' c: matches character c
680   ///   lo '-' hi: matches character c for lo <= c <= hi
681   ///
682   /// Implementations must allocate `entries` to hold all names that need to be
683   /// returned and return the size of `entries`. Caller takes ownership of
684   /// `entries` after the call.
685   ///
686   /// In case of error, the implementations must set `status` to a value
687   /// different than `TF_OK`, free any memory that might have been allocated for
688   /// `entries` and return -1.
689   ///
690   /// The allocation and freeing of memory must happen via the functions sent to
691   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
692   /// structure in Section 4).
693   ///
694   /// Plugins:
695   ///   * Must set `status` to `TF_OK` if all matches were returned.
696   ///   * Might use any other error value for `status` to signal other errors.
697   ///
698   /// DEFAULT IMPLEMENTATION: Scans the directory tree (in parallel if possible)
699   /// and fills `*entries`. Needs `get_children` and `is_directory`.
700   int (*get_matching_paths)(const TF_Filesystem* filesystem, const char* glob,
701                             char*** entries, TF_Status* status);
702 
703   /// Flushes any filesystem cache currently in memory
704   ///
705   /// DEFAULT IMPLEMENTATION: No op.
706   void (*flush_caches)(const TF_Filesystem* filesystem);
707 
708   /// Starts a new transaction.
709   ///
710   /// An opaque transaction token is returned in `token`. Ownership of the token
711   /// is in filesystem. Token will be freed in `end_transaction` call and any
712   /// access to token after that is invalid.
713   ///
714   /// In case of error, plugins must set `status` to a value different than
715   /// `TF_OK`, free memory allocated for `token` and return -1.
716   ///
717   /// The allocation and freeing of memory must happen via the functions sent to
718   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
719   /// structure in Section 4).
720   ///
721   /// Plugins:
722   ///   * Must set `status` to `TF_OK` if transaction successfuly started.
723   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if multiple transactions
724   ///     are not supported
725   ///   * Might use any other error value for `status` to signal other errors.
726   int (*start_transaction)(const TF_Filesystem* filesystem,
727                            TF_TransactionToken** token, TF_Status* status);
728 
729   /// Ends transaction and free the `token`. Any access to token after
730   /// that will be invalid.
731   ///
732   /// In case of error, plugins must set `status` to a value different than
733   /// `TF_OK`, free memory allocated for `token` and return -1.
734   ///
735   /// The allocation and freeing of memory must happen via the functions sent to
736   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
737   /// structure in Section 4).
738   ///
739   /// Plugins:
740   ///   * Must set `status` to `TF_OK` if transaction successfuly finalized.
741   ///   * Must set `status` to `TF_NOT_FOUND` if token is invalid/not found
742   ///   * Might use any other error value for `status` to signal other errors.
743   int (*end_transaction)(const TF_Filesystem* filesystem,
744                          TF_TransactionToken* token, TF_Status* status);
745 
746   /// Adds file/directory in the `path` to transaction in `token`. It is a valid
747   /// operation to add a path that doesn't exist yet to a transaction.
748   ///
749   /// In case of error, plugins must set `status` to a value different than
750   /// `TF_OK`, free memory allocated for `token` and return -1.
751   ///
752   /// The allocation and freeing of memory must happen via the functions sent to
753   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
754   /// structure in Section 4).
755   ///
756   /// Plugins:
757   ///   * Must set `status` to `TF_OK` if path added to transaction successful.
758   ///   * Must set `status` to `TF_NOT_FOUND` if `token` is invalid.
759   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if file/directory is in
760   ///     another transaction and multiple transactions are not supported
761   ///   * Might use any other error value for `status` to signal other errors.
762   int (*add_to_transaction)(const TF_Filesystem* filesystem, const char* path,
763                             TF_TransactionToken* token, TF_Status* status);
764 
765   /// Returns transaction token for file/directory in the `path`. Note that path
766   /// may not exist yet but still might be part of a transaction.
767   ///
768   /// Transaction token is returned in `token`. Ownership of the token is in
769   /// filesystem. Token will be freed in `end_transaction` call and any access
770   /// to token after that is invalid.
771   ///
772   /// In case of error, plugins must set `status` to a value different than
773   /// `TF_OK`, free memory allocated for `token` and return -1.
774   ///
775   /// The allocation and freeing of memory must happen via the functions sent to
776   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
777   /// structure in Section 4).
778   ///
779   /// Plugins:
780   ///   * Must set `status` to `TF_OK` if a transaction for path is found
781   ///   * Must set `status` to `TF_NOT_FOUND` if `path` is not part of any
782   ///     transaction
783   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if `path`  is
784   ///     not in this filesystem.
785   ///   * Might use any other error value for `status` to signal other errors.
786   int (*get_transaction_for_path)(const TF_Filesystem* filesystem,
787                                   const char* path, TF_TransactionToken** token,
788                                   TF_Status* status);
789 
790   /// Returns transaction token for `path` if it is part of a transaction else
791   /// starts a new transaction and adds `path` to that transaction
792   ///
793   /// Transaction token is returned in `token`. Ownership of the token is in
794   /// filesystem. Token will be freed in `end_transaction` call and any access
795   /// to token after that is invalid.
796   ///
797   /// In case of error, plugins must set `status` to a value different than
798   /// `TF_OK`, free memory allocated for `token` and return -1.
799   ///
800   /// The allocation and freeing of memory must happen via the functions sent to
801   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
802   /// structure in Section 4).
803   ///
804   /// Plugins:
805   ///   * Must set `status` to `TF_OK` if transaction found or successfuly
806   ///     started.
807   ///   * Must set `status` to `TF_NOT_FOUND` if `path` doesn't point to this
808   ///     filesystem
809   ///   * Must set `status` to `TF_FAILED_PRECONDITION` if file/directory is
810   ///     not in any transaction and multiple transactions are not supported.
811   ///   * Might use any other error value for `status` to signal other errors.
812   int (*get_or_start_transaction_for_path)(const TF_Filesystem* filesystem,
813                                            const char* path,
814                                            TF_TransactionToken** token,
815                                            TF_Status* status);
816 
817   /// Decodes transaction token in `token` to human readable format for
818   /// debugging.
819   ///
820   /// A new `char*` buffer must be allocated by this method. Core TensorFlow
821   /// manages the lifetime of the buffer after the call. Thus, all callers of
822   /// this method must take ownership of the returned pointer.
823   ///
824   /// Plugins must not return `nullptr`. Returning empty strings is allowed.
825   ///
826   /// The allocation and freeing of memory must happen via the functions sent to
827   /// core TensorFlow upon registration (see the `TF_FilesystemPluginInfo`
828   /// structure in Section 4).
829   ///
830   /// DEFAULT IMPLEMENTATION: Dump token and owner address.
831   char* (*decode_transaction_token)(const TF_Filesystem* filesystem,
832                                     const TF_TransactionToken* token);
833 
834   /// Returns pointer to an array of available configuration options and their
835   /// current/default values in `options` and number of options in array in
836   /// `num_options`. Ownership of the array is transferred to caller and the
837   /// caller is responsible of freeing the buffers using respective file systems
838   /// allocation API.
839   ///
840   /// Plugins:
841   ///   * Must set `status` to `TF_OK` if `options` and `num_options` set.
842   ///     If there is no configurable option, `num_options` should be 0.
843   ///   * Might use any other error value for `status` to signal other errors.
844   ///
845   /// DEFAULT IMPLEMENTATION: return 0 options and `TF_OK`.
846   void (*get_filesystem_configuration)(const TF_Filesystem* filesystem,
847                                        TF_Filesystem_Option** options,
848                                        int* num_options, TF_Status* status);
849 
850   /// Updates filesystem configuration with options passed in `options`. It can
851   /// contain full set of options supported by the filesystem or just a subset
852   /// of them. Ownership of options and buffers therein belongs to the caller
853   /// and any buffers need to be allocated through filesystem allocation API.
854   /// Filesystems may choose to ignore configuration errors but should at least
855   /// display a warning or error message to warn the users.
856   ///
857   /// Plugins:
858   ///   * Must set `status` to `TF_OK` if options are updated.
859   ///   * Might use any other error value for `status` to signal other errors.
860   ///
861   /// DEFAULT IMPLEMENTATION: return `TF_NOT_FOUND`.
862   void (*set_filesystem_configuration)(const TF_Filesystem* filesystem,
863                                        const TF_Filesystem_Option** options,
864                                        int num_options, TF_Status* status);
865 
866   /// Returns the value of the filesystem option given in `key` in `option`.
867   /// Valid values of the `key` are returned by
868   /// `get_file_system_configuration_keys` call. Ownership of the
869   /// `option` is transferred to caller. Buffers therein should be allocated and
870   /// freed by the relevant filesystems allocation API.
871   ///
872   /// Plugins:
873   ///   * Must set `status` to `TF_OK` if `option` is set
874   ///   * Must set `status` to `TF_NOT_FOUND` if the key is invalid
875   ///   * Might use any other error value for `status` to signal other errors.
876   ///
877   /// DEFAULT IMPLEMENTATION: return `TF_NOT_FOUND`.
878   void (*get_filesystem_configuration_option)(const TF_Filesystem* filesystem,
879                                               const char* key,
880                                               TF_Filesystem_Option** option,
881                                               TF_Status* status);
882 
883   /// Sets the value of the filesystem option given in `key` to value in
884   /// `option`. Valid values of the `key` are returned by
885   /// `get_file_system_configuration_keys` call. Ownership of the `option` and
886   /// the `key` belogs to the caller. Buffers therein should be allocated and
887   /// freed by the filesystems allocation API.
888   ///
889   /// Plugins:
890   ///   * Must set `status` to `TF_OK` if `option` is set/updated
891   ///   * Must set `status` to `TF_NOT_FOUND` if the key is invalid
892   ///   * Might use any other error value for `status` to signal other errors.
893   ///
894   /// DEFAULT IMPLEMENTATION: return `TF_NOT_FOUND`.
895   void (*set_filesystem_configuration_option)(
896       const TF_Filesystem* filesystem, const TF_Filesystem_Option* option,
897       TF_Status* status);
898 
899   /// Returns a list of valid configuration keys in `keys` array and number of
900   /// keys in `num_keys`. Ownership of the buffers in `keys` are transferred to
901   /// caller and needs to be freed using relevant filesystem allocation API.
902   ///
903   /// Plugins:
904   ///   * Must set `status` to `TF_OK` on success. If there are no configurable
905   ///     keys, `num_keys` should be set to 0
906   ///   * Might use any other error value for `status` to signal other errors.
907   ///
908   /// DEFAULT IMPLEMENTATION: return `TF_OK` and `num_keys`=0.
909   void (*get_filesystem_configuration_keys)(const TF_Filesystem* filesystem,
910                                             char** keys, int* num_keys,
911                                             TF_Status* status);
912 
913 } TF_FilesystemOps;
914 // LINT.ThenChange(:filesystem_ops_version)
915 
916 /// SECTION 3. ABI and API compatibility
917 /// ----------------------------------------------------------------------------
918 ///
919 /// In this section we define constants and macros to record versioning
920 /// information for each of the structures in section 2: ABI and API versions
921 /// and the number of functions in each of the function tables (which is
922 /// automatically determined, so ignored for the rest of this comment).
923 ///
924 /// Since filesystem plugins are outside of TensorFlow's code tree, they are not
925 /// tied with TensorFlow releases and should have their own versioning metadata
926 /// in addition with the data discussed in this section. Each plugin author can
927 /// use a custom scheme, but it should only relate to changes in plugin code.
928 /// This section only touches metadata related to the versioning of this
929 /// interface that is shared by all possible plugins.
930 ///
931 /// The API number increases whenever we break API compatibility while still
932 /// maintaining ABI compatibility. This happens only in the following cases:
933 ///   1. A new method is added _at the end_ of the function table.
934 ///   2. Preconditions or postconditions for one operation in these function
935 ///   table change. Note that only core TensorFlow is able to impose these
936 ///   invariants (i.e., guarantee the preconditions before calling the operation
937 ///   and check the postconditions after the operation returns). If plugins need
938 ///   additional invariants, they should be checked on the plugin side and the
939 ///   `status` out variable should be updated accordingly (e.g., to include
940 ///   plugin version information that relates to the condition change).
941 ///
942 /// All other changes to the data structures (e.g., method removal, method
943 /// reordering, argument reordering, adding or removing arguments, changing the
944 /// type or the constness of a parameter, etc.) results in an ABI breakage.
945 /// Thus, we should not do any of these types of changes, except, potentially,
946 /// when we are releasing a new major version of TensorFlow. This is an escape
947 /// hatch, to be used rarely, preferably only to cleanup these structures.
948 /// Whenever we do these changes, the ABI number must be increased.
949 ///
950 /// Next section will detail how this metadata is used at plugin registration to
951 /// only load compatible plugins and discard all others.
952 
953 // LINT.IfChange(random_access_file_ops_version)
954 constexpr int TF_RANDOM_ACCESS_FILE_OPS_API = 0;
955 constexpr int TF_RANDOM_ACCESS_FILE_OPS_ABI = 0;
956 constexpr size_t TF_RANDOM_ACCESS_FILE_OPS_SIZE =
957     sizeof(TF_RandomAccessFileOps);
958 // LINT.ThenChange()
959 
960 // LINT.IfChange(writable_file_ops_version)
961 constexpr int TF_WRITABLE_FILE_OPS_API = 0;
962 constexpr int TF_WRITABLE_FILE_OPS_ABI = 0;
963 constexpr size_t TF_WRITABLE_FILE_OPS_SIZE = sizeof(TF_WritableFileOps);
964 // LINT.ThenChange()
965 
966 // LINT.IfChange(read_only_memory_region_ops_version)
967 constexpr int TF_READ_ONLY_MEMORY_REGION_OPS_API = 0;
968 constexpr int TF_READ_ONLY_MEMORY_REGION_OPS_ABI = 0;
969 constexpr size_t TF_READ_ONLY_MEMORY_REGION_OPS_SIZE =
970     sizeof(TF_ReadOnlyMemoryRegionOps);
971 // LINT.ThenChange()
972 
973 // LINT.IfChange(filesystem_ops_version)
974 constexpr int TF_FILESYSTEM_OPS_API = 0;
975 constexpr int TF_FILESYSTEM_OPS_ABI = 0;
976 constexpr size_t TF_FILESYSTEM_OPS_SIZE = sizeof(TF_FilesystemOps);
977 // LINT.ThenChange()
978 
979 /// SECTION 4. Plugin registration and initialization
980 /// ----------------------------------------------------------------------------
981 ///
982 /// In this section we define the API used by core TensorFlow to initialize a
983 /// filesystem provided by a plugin. That is, we define the following:
984 ///   * `TF_InitPlugin` function: must be present in the plugin shared object as
985 ///     it will be called by core TensorFlow when the filesystem plugin is
986 ///     loaded;
987 ///   * `TF_FilesystemPluginOps` struct: used to transfer information between
988 ///     plugins and core TensorFlow about the operations provided and metadata;
989 ///   * `TF_FilesystemPluginInfo` struct: similar to the above structure, but
990 ///     collects information about all the file schemes that the plugin provides
991 ///     support for, as well as about the plugin's memory handling routines;
992 ///   * `TF_SetFilesystemVersionMetadata` function: must be called by plugins in
993 ///     their `TF_InitPlugin` to record the versioning information the plugins
994 ///     are compiled against.
995 ///
996 /// The `TF_InitPlugin` function is used by plugins to set up the data
997 /// structures that implement this interface, as presented in Section 2. In
998 /// order to not have plugin shared objects call back symbols defined in core
999 /// TensorFlow, `TF_InitPlugin` has a `TF_FilesystemPluginInfo` argument which
1000 /// the plugin must fill (using the `TF_SetFilesystemVersionMetadata` for the
1001 /// metadata and setting up all the supported operations and the URI schemes
1002 /// that are supported).
1003 
1004 /// This structure incorporates the operations defined in Section 2 and the
1005 /// metadata defined in section 3, allowing plugins to define different ops
1006 /// for different URI schemes.
1007 ///
1008 /// Every URI scheme is of the form "fs" for URIs of form "fs:///path/to/file".
1009 /// For local filesystems (i.e., when the URI is "/path/to/file"), the scheme
1010 /// must be "". The scheme must never be `nullptr`.
1011 ///
1012 /// Every plugin fills this in `TF_InitPlugin`, using the alocator passed as
1013 /// argument to allocate memory. After `TF_InitPlugin` finishes, core
1014 /// TensorFlow uses the information present in this to initialize filesystems
1015 /// for the URI schemes that the plugin requests.
1016 ///
1017 /// All pointers defined in this structure point to memory allocated by the DSO
1018 /// using an allocator provided by core TensorFlow when calling `TF_InitPlugin`.
1019 ///
1020 /// IMPORTANT: To maintain binary compatibility, the layout of this structure
1021 /// must not change! In the unlikely case that a new type of file needs to be
1022 /// supported, add the new ops and metadata at the end of the structure.
1023 typedef struct TF_FilesystemPluginOps {
1024   char* scheme;
1025   int filesystem_ops_abi;
1026   int filesystem_ops_api;
1027   size_t filesystem_ops_size;
1028   TF_FilesystemOps* filesystem_ops;
1029   int random_access_file_ops_abi;
1030   int random_access_file_ops_api;
1031   size_t random_access_file_ops_size;
1032   TF_RandomAccessFileOps* random_access_file_ops;
1033   int writable_file_ops_abi;
1034   int writable_file_ops_api;
1035   size_t writable_file_ops_size;
1036   TF_WritableFileOps* writable_file_ops;
1037   int read_only_memory_region_ops_abi;
1038   int read_only_memory_region_ops_api;
1039   size_t read_only_memory_region_ops_size;
1040   TF_ReadOnlyMemoryRegionOps* read_only_memory_region_ops;
1041 } TF_FilesystemPluginOps;
1042 
1043 /// This structure gathers together all the operations provided by the plugin.
1044 ///
1045 /// Plugins must provide exactly `num_schemes` elements in the `ops` array.
1046 ///
1047 /// Since memory that is allocated by the DSO gets transferred to core
1048 /// TensorFlow, we need to provide a way for the allocation and deallocation to
1049 /// match. This is why this structure also defines `plugin_memory_allocate` and
1050 /// `plugin_memory_free` members.
1051 ///
1052 /// All memory allocated by the plugin that will be owned by core TensorFlow
1053 /// must be allocated using the allocator in this structure. Core TensorFlow
1054 /// will use the deallocator to free this memory once it no longer needs it.
1055 ///
1056 /// IMPORTANT: To maintain binary compatibility, the layout of this structure
1057 /// must not change! In the unlikely case that new global operations must be
1058 /// provided, add them at the end of the structure.
1059 typedef struct TF_FilesystemPluginInfo {
1060   size_t num_schemes;
1061   TF_FilesystemPluginOps* ops;
1062   void* (*plugin_memory_allocate)(size_t size);
1063   void (*plugin_memory_free)(void* ptr);
1064 } TF_FilesystemPluginInfo;
1065 
1066 /// Convenience function for setting the versioning metadata.
1067 ///
1068 /// The argument is guaranteed to not be `nullptr`.
1069 ///
1070 /// We want this to be defined in the plugin's memory space and we guarantee
1071 /// that core TensorFlow will never call this.
TF_SetFilesystemVersionMetadata(TF_FilesystemPluginOps * ops)1072 static inline void TF_SetFilesystemVersionMetadata(
1073     TF_FilesystemPluginOps* ops) {
1074   ops->filesystem_ops_abi = TF_FILESYSTEM_OPS_ABI;
1075   ops->filesystem_ops_api = TF_FILESYSTEM_OPS_API;
1076   ops->filesystem_ops_size = TF_FILESYSTEM_OPS_SIZE;
1077   ops->random_access_file_ops_abi = TF_RANDOM_ACCESS_FILE_OPS_ABI;
1078   ops->random_access_file_ops_api = TF_RANDOM_ACCESS_FILE_OPS_API;
1079   ops->random_access_file_ops_size = TF_RANDOM_ACCESS_FILE_OPS_SIZE;
1080   ops->writable_file_ops_abi = TF_WRITABLE_FILE_OPS_ABI;
1081   ops->writable_file_ops_api = TF_WRITABLE_FILE_OPS_API;
1082   ops->writable_file_ops_size = TF_WRITABLE_FILE_OPS_SIZE;
1083   ops->read_only_memory_region_ops_abi = TF_READ_ONLY_MEMORY_REGION_OPS_ABI;
1084   ops->read_only_memory_region_ops_api = TF_READ_ONLY_MEMORY_REGION_OPS_API;
1085   ops->read_only_memory_region_ops_size = TF_READ_ONLY_MEMORY_REGION_OPS_SIZE;
1086 }
1087 
1088 /// Initializes a TensorFlow plugin.
1089 ///
1090 /// Must be implemented by the plugin DSO. It is called by TensorFlow runtime.
1091 ///
1092 /// Filesystem plugins can be loaded on demand by users via
1093 /// `Env::LoadLibrary` or during TensorFlow's startup if they are on certain
1094 /// paths (although this has a security risk if two plugins register for the
1095 /// same filesystem and the malicious one loads before the legimitate one -
1096 /// but we consider this to be something that users should care about and
1097 /// manage themselves). In both of these cases, core TensorFlow looks for
1098 /// the `TF_InitPlugin` symbol and calls this function.
1099 ///
1100 /// For every filesystem URI scheme that this plugin supports, the plugin must
1101 /// add one `TF_FilesystemPluginInfo` entry in `plugin_info->ops` and call
1102 /// `TF_SetFilesystemVersionMetadata` for that entry.
1103 ///
1104 /// Plugins must also initialize `plugin_info->plugin_memory_allocate` and
1105 /// `plugin_info->plugin_memory_free` to ensure memory allocated by plugin is
1106 /// freed in a compatible way.
1107 TF_CAPI_EXPORT extern void TF_InitPlugin(TF_FilesystemPluginInfo* plugin_info);
1108 
1109 #ifdef __cplusplus
1110 }  // end extern "C"
1111 #endif  // __cplusplus
1112 
1113 #endif  // TENSORFLOW_C_EXPERIMENTAL_FILESYSTEM_FILESYSTEM_INTERFACE_H_
1114