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