paul@183 | 1 | = Client Library = |
paul@183 | 2 | |
paul@183 | 3 | The filesystem client library offers abstractions and a number of layers of |
paul@183 | 4 | functionality to support interaction with [[Components|components]] and the |
paul@398 | 5 | provision of higher-level mechanisms and abstractions for file access. In |
paul@398 | 6 | particular, the client library is intended for use by a conventional C |
paul@398 | 7 | library, with the functions in the C library invoking client library functions |
paul@398 | 8 | and employing client library structures internally. |
paul@183 | 9 | |
paul@457 | 10 | The client library is provided by [[Libraries#libfsclient|`libfsclient`]] |
paul@457 | 11 | within the `departure` package. |
paul@427 | 12 | |
paul@183 | 13 | <<TableOfContents(2,3)>> |
paul@183 | 14 | |
paul@402 | 15 | == Header Files == |
paul@402 | 16 | |
paul@402 | 17 | The following header files are pertinent to client library usage: |
paul@402 | 18 | |
paul@402 | 19 | || '''File''' || '''Description''' || |
paul@402 | 20 | || `fsclient/client.h` || Client functions and data structures || |
paul@402 | 21 | || `systypes/fcntl.h` || Flag values for opening operations || |
paul@402 | 22 | |
paul@183 | 23 | == File Data Structures == |
paul@183 | 24 | |
paul@183 | 25 | Since files are accessed using file references, the `file_t` data structure is |
paul@183 | 26 | used to wrap such references and other relevant state. Thus, such structures |
paul@183 | 27 | can be broadly regarded as similar to the traditional `FILE` data structure. |
paul@183 | 28 | |
paul@402 | 29 | The members of the `file_t` data structure are as follows: |
paul@183 | 30 | |
paul@457 | 31 | || '''Member''' || '''Description''' || |
paul@391 | 32 | || `ref` || A reference to the component providing file content || |
paul@183 | 33 | || `memory` || The memory address of the exposed file region || |
paul@183 | 34 | || `start_pos` || The start position of the region in the file || |
paul@183 | 35 | || `end_pos` || The end position of the region in the file || |
paul@183 | 36 | || `data_end` || The amount or extent of populated data in the region || |
paul@183 | 37 | || `data_current` || The offset used to track client position in the region || |
paul@183 | 38 | || `size` || The total size of the file || |
paul@183 | 39 | || `object_flags` || Flags indicating support for certain file features || |
paul@183 | 40 | || `can_block` || Notification flags for blocking access to the file || |
paul@183 | 41 | || `notifications`|| Notification flags set for the file || |
paul@391 | 42 | || `flags` || The flags used to open the file || |
paul@402 | 43 | || `error` || Any failure or error condition on the file || |
paul@402 | 44 | |
paul@402 | 45 | Generally, these members should not be accessed directly, mostly being |
paul@402 | 46 | reserved for use by the client library itself. |
paul@183 | 47 | |
paul@183 | 48 | == Client Programming Interface == |
paul@183 | 49 | |
paul@183 | 50 | The client programming interface provides functions somewhat resembling the |
paul@183 | 51 | traditional C library and low-level Unix interfaces for file access, and these |
paul@183 | 52 | functions are intended to support such traditional interfaces. |
paul@183 | 53 | |
paul@183 | 54 | === Files === |
paul@183 | 55 | |
paul@402 | 56 | Files are opened using the following function returning a file data structure: |
paul@183 | 57 | |
paul@183 | 58 | {{{ |
paul@183 | 59 | file_t *client_open(const char *name, flags_t flags); |
paul@183 | 60 | }}} |
paul@183 | 61 | |
paul@402 | 62 | To test whether the file was successfully open, the following function should |
paul@402 | 63 | be invoked, this returning a true (non-zero) value if the file was |
paul@402 | 64 | successfully opened: |
paul@402 | 65 | |
paul@402 | 66 | {{{ |
paul@402 | 67 | int client_opened(file_t *file); |
paul@402 | 68 | }}} |
paul@402 | 69 | |
paul@402 | 70 | Each file should be closed using `client_close` regardless of whether the file |
paul@402 | 71 | was successfully opened. |
paul@402 | 72 | |
paul@402 | 73 | ==== Example ==== |
paul@402 | 74 | |
paul@402 | 75 | {{{ |
paul@402 | 76 | file_t *file = client_open("somefile", O_RDONLY); |
paul@402 | 77 | |
paul@402 | 78 | if (client_opened(file)) |
paul@402 | 79 | { |
paul@402 | 80 | /* Perform operations on file. */ |
paul@402 | 81 | } |
paul@402 | 82 | |
paul@402 | 83 | client_close(file); |
paul@402 | 84 | }}} |
paul@183 | 85 | |
paul@183 | 86 | === Pipes === |
paul@183 | 87 | |
paul@402 | 88 | Pipes are opened using a special function returning an error code, setting the |
paul@402 | 89 | supplied reader and writer endpoint parameters: |
paul@183 | 90 | |
paul@183 | 91 | {{{ |
paul@183 | 92 | long client_pipe(file_t **reader, file_t **writer, flags_t flags); |
paul@183 | 93 | }}} |
paul@183 | 94 | |
paul@402 | 95 | Each returned pipe endpoint may be closed using `client_close`. If an error |
paul@402 | 96 | condition is reported using a non-zero value, the endpoints will not be |
paul@402 | 97 | retained and will therefore not need to be closed: the error condition is |
paul@402 | 98 | communicated purely via the return value. |
paul@402 | 99 | |
paul@402 | 100 | ==== Example ==== |
paul@402 | 101 | |
paul@402 | 102 | {{{ |
paul@402 | 103 | file_t *reader, *writer; |
paul@402 | 104 | |
paul@402 | 105 | if (!client_pipe(&reader, &writer, 0)) |
paul@402 | 106 | { |
paul@402 | 107 | /* Perform operations on pipe. */ |
paul@402 | 108 | } |
paul@402 | 109 | |
paul@402 | 110 | client_close(reader); |
paul@402 | 111 | client_close(writer); |
paul@402 | 112 | }}} |
paul@183 | 113 | |
paul@183 | 114 | === Closing Files and Pipes === |
paul@183 | 115 | |
paul@183 | 116 | Closing files and pipes involves a common operation: |
paul@183 | 117 | |
paul@183 | 118 | {{{ |
paul@183 | 119 | void client_close(file_t *file); |
paul@183 | 120 | }}} |
paul@183 | 121 | |
paul@183 | 122 | When client programs terminate, the freeing of their object capabilities |
paul@183 | 123 | should cause the closure of files and pipes, but programs may choose to close |
paul@183 | 124 | such resources explicitly. |
paul@183 | 125 | |
paul@183 | 126 | === Reading and Writing === |
paul@183 | 127 | |
paul@183 | 128 | Reading and writing files and pipes involves functions resembling the |
paul@183 | 129 | traditional low-level `read` and `write` functions: |
paul@183 | 130 | |
paul@183 | 131 | {{{ |
paul@183 | 132 | offset_t client_read(file_t *file, void *buf, offset_t count); |
paul@183 | 133 | offset_t client_write(file_t *file, const void *buf, offset_t count); |
paul@183 | 134 | }}} |
paul@183 | 135 | |
paul@183 | 136 | === Navigation in Files === |
paul@183 | 137 | |
paul@183 | 138 | Support for navigation in files is provided using functions resembling the |
paul@183 | 139 | traditional higher-level `fseek` and `ftell` functions: |
paul@183 | 140 | |
paul@183 | 141 | {{{ |
paul@183 | 142 | offset_t client_seek(file_t *file, offset_t offset, int whence); |
paul@183 | 143 | long client_tell(file_t *file); |
paul@183 | 144 | }}} |
paul@183 | 145 | |
paul@183 | 146 | === Accessing Exposed Memory Regions === |
paul@183 | 147 | |
paul@183 | 148 | Although the client library (and the provision of files) employs mapped |
paul@183 | 149 | memory, a function can be used to explicitly reference memory for file access: |
paul@183 | 150 | |
paul@183 | 151 | {{{ |
paul@402 | 152 | void *client_mmap(file_t *file, offset_t position, offset_t length, |
paul@402 | 153 | offset_t start_visible, offset_t end_visible, |
paul@402 | 154 | l4re_rm_flags_t region_flags); |
paul@183 | 155 | }}} |
paul@183 | 156 | |
paul@402 | 157 | Here, a portion of the indicated `file` is exposed in a memory region, with |
paul@402 | 158 | the memory region corresponding to the contents of the file starting at the |
paul@402 | 159 | given `position` in the file and having a span of the given `length` in bytes. |
paul@402 | 160 | |
paul@402 | 161 | Additional parameters control masking of the file content. If `start_visible` |
paul@402 | 162 | and `end_visible` are both non-zero, then they indicate a visible span in the |
paul@402 | 163 | file. Such limits are applied to the mapped region, with locations |
paul@402 | 164 | corresponding to positions before `start_visible` and positions after |
paul@402 | 165 | `end_visible` yielding zero byte values. |
paul@402 | 166 | |
paul@402 | 167 | The `region_flags` indicate the memory access properties of the mapped region |
paul@402 | 168 | in the task obtaining it. For example, where a region will be populated with |
paul@402 | 169 | code, the `L4RE_DS_F_RX` flag would be appropriate, this indicating that the |
paul@402 | 170 | region will be read and also have its contents run or executed. |
paul@402 | 171 | |
paul@402 | 172 | '''Note:''' Currently, when masking is applied to a file, any write operations |
paul@402 | 173 | will cause the modified memory to be copied and modified, as opposed to |
paul@402 | 174 | causing modifications to the underlying file content. This behaviour may |
paul@402 | 175 | eventually be more configurable. |
paul@402 | 176 | |
paul@402 | 177 | === Accessing Pipe Content in Memory Regions === |
paul@402 | 178 | |
paul@183 | 179 | Pipes support a different mechanism for navigation involving the following |
paul@183 | 180 | functions: |
paul@183 | 181 | |
paul@183 | 182 | {{{ |
paul@183 | 183 | long client_current_region(file_t *file); |
paul@183 | 184 | long client_next_region(file_t *file); |
paul@183 | 185 | }}} |
paul@183 | 186 | |
paul@183 | 187 | Such navigation functions for files and pipes do not need to be used where the |
paul@183 | 188 | higher-level reading, writing and seeking functions are in use. |
paul@183 | 189 | |
paul@183 | 190 | === Flushing and Synchronisation === |
paul@183 | 191 | |
paul@183 | 192 | For synchronisation purposes, either with the filesystem itself or with other |
paul@183 | 193 | users of the filesystem, a function resembling the traditional `fflush` |
paul@183 | 194 | function is provided: |
paul@183 | 195 | |
paul@183 | 196 | {{{ |
paul@183 | 197 | long client_flush(file_t *file); |
paul@183 | 198 | }}} |
paul@183 | 199 | |
paul@183 | 200 | This updates the file data structure with new details of the file size, also |
paul@183 | 201 | updating any altered details of the extent of the data in the currently |
paul@183 | 202 | accessed region of the file. |
paul@183 | 203 | |
paul@183 | 204 | === Notifications === |
paul@183 | 205 | |
paul@457 | 206 | Since files and pipes may be accessed by multiple clients, this being of |
paul@457 | 207 | particular significance for any real use of pipes, notifications can be |
paul@457 | 208 | configured to communicate a change in state to other users of these resources |
paul@457 | 209 | when they are accessed. Directories can also be monitored using notifications. |
paul@183 | 210 | |
paul@183 | 211 | Notification types are specified using values encoding a number of flags, and |
paul@183 | 212 | the following flags are available for this purpose: |
paul@183 | 213 | |
paul@183 | 214 | || '''Flag''' || '''Notification Type''' || |
paul@183 | 215 | || `NOTIFY_CONTENT_AVAILABLE` || Content available to read || |
paul@457 | 216 | || `NOTIFY_FILE_OPENED` || File opened in directory || |
paul@183 | 217 | || `NOTIFY_PEER_CLOSED` || Other party has closed their endpoint || |
paul@183 | 218 | || `NOTIFY_SPACE_AVAILABLE` || Space available for writing || |
paul@183 | 219 | |
paul@457 | 220 | The delivery of notifications is requested by subscribing to notifications for |
paul@457 | 221 | a given resource via a notifier object: |
paul@457 | 222 | |
paul@457 | 223 | {{{ |
paul@457 | 224 | long client_subscribe(file_t *file, notify_flags_t flags, file_notifier_t *notifier); |
paul@457 | 225 | }}} |
paul@457 | 226 | |
paul@457 | 227 | A notifier object can be common throughout all threads in a task, being |
paul@457 | 228 | obtained using the following function: |
paul@457 | 229 | |
paul@457 | 230 | {{{ |
paul@457 | 231 | file_notifier_t *client_notifier_task(); |
paul@457 | 232 | }}} |
paul@457 | 233 | |
paul@457 | 234 | Alternatively, a local notifier can be created for use within a thread: |
paul@457 | 235 | |
paul@457 | 236 | {{{ |
paul@457 | 237 | file_notifier_t *client_notifier_local(); |
paul@457 | 238 | }}} |
paul@457 | 239 | |
paul@457 | 240 | Local notifiers must be closed when they are no longer needed: |
paul@457 | 241 | |
paul@457 | 242 | {{{ |
paul@457 | 243 | void client_notifier_close(file_notifier_t *notifier); |
paul@457 | 244 | }}} |
paul@457 | 245 | |
paul@457 | 246 | When notifications are no longer needed, an unsubscribe operation can be |
paul@457 | 247 | invoked: |
paul@457 | 248 | |
paul@457 | 249 | {{{ |
paul@457 | 250 | long client_unsubscribe(file_t *file, file_notifier_t *notifier); |
paul@457 | 251 | }}} |
paul@457 | 252 | |
paul@457 | 253 | ==== Example ==== |
paul@457 | 254 | |
paul@457 | 255 | {{{ |
paul@457 | 256 | file_notifier_t *notifier = client_notifier_local(); |
paul@457 | 257 | file_t *directory = client_open(filename, O_DIRECTORY); |
paul@457 | 258 | |
paul@457 | 259 | if (client_opened(directory)) |
paul@457 | 260 | { |
paul@457 | 261 | if (!client_subscribe(directory, NOTIFY_FILE_OPENED, notifier)) |
paul@457 | 262 | { |
paul@457 | 263 | if (!client_wait_file(directory, notifier)) |
paul@457 | 264 | { |
paul@457 | 265 | /* File opened in directory. */ |
paul@457 | 266 | } |
paul@457 | 267 | } |
paul@457 | 268 | } |
paul@457 | 269 | |
paul@457 | 270 | client_close(directory); |
paul@457 | 271 | client_notifier_close(notifier); |
paul@457 | 272 | }}} |
paul@457 | 273 | |
paul@183 | 274 | === Blocking Operations === |
paul@183 | 275 | |
paul@183 | 276 | Reading and writing operations can be configured to block if data cannot be |
paul@183 | 277 | read or written respectively. The following function is provided for this |
paul@183 | 278 | purpose: |
paul@183 | 279 | |
paul@183 | 280 | {{{ |
paul@183 | 281 | long client_set_blocking(file_t *file, notify_flags_t flags); |
paul@183 | 282 | }}} |
paul@183 | 283 | |
paul@183 | 284 | For pipes, blocking behaviour is the default and must be disabled explicitly, |
paul@183 | 285 | either by opening using the `O_NONBLOCK` flag or by calling |
paul@183 | 286 | `client_set_blocking` with no flags set. |
paul@457 | 287 | |
paul@457 | 288 | Blocking behaviour is supported using the notification functionality. When |
paul@457 | 289 | access to a file or pipe cannot be satisfied for a particular operation, such |
paul@457 | 290 | as reading not being able to yield more content or writing not being able to |
paul@457 | 291 | submit more content, the task-level notifier will be used to wait for |
paul@457 | 292 | notifications applicable to the file or pipe involved. Consequently, the |
paul@457 | 293 | access will effectively block until notifications are delivered indicating |
paul@457 | 294 | that the state of the file or pipe has changed, and until it is determined |
paul@457 | 295 | that the change of state will allow the operation to proceed successfully. |