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@476 | 62 | New instances of open files, employing potentially different flags, can be |
paul@476 | 63 | obtained using the following function: |
paul@476 | 64 | |
paul@476 | 65 | {{{ |
paul@476 | 66 | file_t *client_reopen(file_t *file, flags_t flags); |
paul@476 | 67 | }}} |
paul@476 | 68 | |
paul@402 | 69 | To test whether the file was successfully open, the following function should |
paul@402 | 70 | be invoked, this returning a true (non-zero) value if the file was |
paul@402 | 71 | successfully opened: |
paul@402 | 72 | |
paul@402 | 73 | {{{ |
paul@402 | 74 | int client_opened(file_t *file); |
paul@402 | 75 | }}} |
paul@402 | 76 | |
paul@402 | 77 | Each file should be closed using `client_close` regardless of whether the file |
paul@402 | 78 | was successfully opened. |
paul@402 | 79 | |
paul@402 | 80 | ==== Example ==== |
paul@402 | 81 | |
paul@402 | 82 | {{{ |
paul@402 | 83 | file_t *file = client_open("somefile", O_RDONLY); |
paul@402 | 84 | |
paul@402 | 85 | if (client_opened(file)) |
paul@402 | 86 | { |
paul@402 | 87 | /* Perform operations on file. */ |
paul@402 | 88 | } |
paul@402 | 89 | |
paul@402 | 90 | client_close(file); |
paul@402 | 91 | }}} |
paul@183 | 92 | |
paul@183 | 93 | === Pipes === |
paul@183 | 94 | |
paul@402 | 95 | Pipes are opened using a special function returning an error code, setting the |
paul@402 | 96 | supplied reader and writer endpoint parameters: |
paul@183 | 97 | |
paul@183 | 98 | {{{ |
paul@183 | 99 | long client_pipe(file_t **reader, file_t **writer, flags_t flags); |
paul@183 | 100 | }}} |
paul@183 | 101 | |
paul@402 | 102 | Each returned pipe endpoint may be closed using `client_close`. If an error |
paul@402 | 103 | condition is reported using a non-zero value, the endpoints will not be |
paul@402 | 104 | retained and will therefore not need to be closed: the error condition is |
paul@402 | 105 | communicated purely via the return value. |
paul@402 | 106 | |
paul@402 | 107 | ==== Example ==== |
paul@402 | 108 | |
paul@402 | 109 | {{{ |
paul@402 | 110 | file_t *reader, *writer; |
paul@402 | 111 | |
paul@402 | 112 | if (!client_pipe(&reader, &writer, 0)) |
paul@402 | 113 | { |
paul@402 | 114 | /* Perform operations on pipe. */ |
paul@402 | 115 | } |
paul@402 | 116 | |
paul@402 | 117 | client_close(reader); |
paul@402 | 118 | client_close(writer); |
paul@402 | 119 | }}} |
paul@183 | 120 | |
paul@183 | 121 | === Closing Files and Pipes === |
paul@183 | 122 | |
paul@183 | 123 | Closing files and pipes involves a common operation: |
paul@183 | 124 | |
paul@183 | 125 | {{{ |
paul@183 | 126 | void client_close(file_t *file); |
paul@183 | 127 | }}} |
paul@183 | 128 | |
paul@183 | 129 | When client programs terminate, the freeing of their object capabilities |
paul@183 | 130 | should cause the closure of files and pipes, but programs may choose to close |
paul@183 | 131 | such resources explicitly. |
paul@183 | 132 | |
paul@183 | 133 | === Reading and Writing === |
paul@183 | 134 | |
paul@183 | 135 | Reading and writing files and pipes involves functions resembling the |
paul@183 | 136 | traditional low-level `read` and `write` functions: |
paul@183 | 137 | |
paul@183 | 138 | {{{ |
paul@183 | 139 | offset_t client_read(file_t *file, void *buf, offset_t count); |
paul@183 | 140 | offset_t client_write(file_t *file, const void *buf, offset_t count); |
paul@183 | 141 | }}} |
paul@183 | 142 | |
paul@183 | 143 | === Navigation in Files === |
paul@183 | 144 | |
paul@183 | 145 | Support for navigation in files is provided using functions resembling the |
paul@183 | 146 | traditional higher-level `fseek` and `ftell` functions: |
paul@183 | 147 | |
paul@183 | 148 | {{{ |
paul@183 | 149 | offset_t client_seek(file_t *file, offset_t offset, int whence); |
paul@183 | 150 | long client_tell(file_t *file); |
paul@183 | 151 | }}} |
paul@183 | 152 | |
paul@183 | 153 | === Accessing Exposed Memory Regions === |
paul@183 | 154 | |
paul@183 | 155 | Although the client library (and the provision of files) employs mapped |
paul@183 | 156 | memory, a function can be used to explicitly reference memory for file access: |
paul@183 | 157 | |
paul@183 | 158 | {{{ |
paul@402 | 159 | void *client_mmap(file_t *file, offset_t position, offset_t length, |
paul@402 | 160 | offset_t start_visible, offset_t end_visible, |
paul@402 | 161 | l4re_rm_flags_t region_flags); |
paul@183 | 162 | }}} |
paul@183 | 163 | |
paul@402 | 164 | Here, a portion of the indicated `file` is exposed in a memory region, with |
paul@402 | 165 | the memory region corresponding to the contents of the file starting at the |
paul@402 | 166 | given `position` in the file and having a span of the given `length` in bytes. |
paul@402 | 167 | |
paul@402 | 168 | Additional parameters control masking of the file content. If `start_visible` |
paul@402 | 169 | and `end_visible` are both non-zero, then they indicate a visible span in the |
paul@402 | 170 | file. Such limits are applied to the mapped region, with locations |
paul@402 | 171 | corresponding to positions before `start_visible` and positions after |
paul@402 | 172 | `end_visible` yielding zero byte values. |
paul@402 | 173 | |
paul@402 | 174 | The `region_flags` indicate the memory access properties of the mapped region |
paul@402 | 175 | in the task obtaining it. For example, where a region will be populated with |
paul@402 | 176 | code, the `L4RE_DS_F_RX` flag would be appropriate, this indicating that the |
paul@402 | 177 | region will be read and also have its contents run or executed. |
paul@402 | 178 | |
paul@402 | 179 | '''Note:''' Currently, when masking is applied to a file, any write operations |
paul@402 | 180 | will cause the modified memory to be copied and modified, as opposed to |
paul@402 | 181 | causing modifications to the underlying file content. This behaviour may |
paul@402 | 182 | eventually be more configurable. |
paul@402 | 183 | |
paul@402 | 184 | === Accessing Pipe Content in Memory Regions === |
paul@402 | 185 | |
paul@183 | 186 | Pipes support a different mechanism for navigation involving the following |
paul@183 | 187 | functions: |
paul@183 | 188 | |
paul@183 | 189 | {{{ |
paul@183 | 190 | long client_current_region(file_t *file); |
paul@183 | 191 | long client_next_region(file_t *file); |
paul@183 | 192 | }}} |
paul@183 | 193 | |
paul@183 | 194 | Such navigation functions for files and pipes do not need to be used where the |
paul@183 | 195 | higher-level reading, writing and seeking functions are in use. |
paul@183 | 196 | |
paul@183 | 197 | === Flushing and Synchronisation === |
paul@183 | 198 | |
paul@183 | 199 | For synchronisation purposes, either with the filesystem itself or with other |
paul@183 | 200 | users of the filesystem, a function resembling the traditional `fflush` |
paul@183 | 201 | function is provided: |
paul@183 | 202 | |
paul@183 | 203 | {{{ |
paul@183 | 204 | long client_flush(file_t *file); |
paul@183 | 205 | }}} |
paul@183 | 206 | |
paul@183 | 207 | This updates the file data structure with new details of the file size, also |
paul@183 | 208 | updating any altered details of the extent of the data in the currently |
paul@183 | 209 | accessed region of the file. |
paul@183 | 210 | |
paul@183 | 211 | === Notifications === |
paul@183 | 212 | |
paul@457 | 213 | Since files and pipes may be accessed by multiple clients, this being of |
paul@457 | 214 | particular significance for any real use of pipes, notifications can be |
paul@457 | 215 | configured to communicate a change in state to other users of these resources |
paul@457 | 216 | when they are accessed. Directories can also be monitored using notifications. |
paul@183 | 217 | |
paul@183 | 218 | Notification types are specified using values encoding a number of flags, and |
paul@183 | 219 | the following flags are available for this purpose: |
paul@183 | 220 | |
paul@183 | 221 | || '''Flag''' || '''Notification Type''' || |
paul@183 | 222 | || `NOTIFY_CONTENT_AVAILABLE` || Content available to read || |
paul@457 | 223 | || `NOTIFY_FILE_OPENED` || File opened in directory || |
paul@183 | 224 | || `NOTIFY_PEER_CLOSED` || Other party has closed their endpoint || |
paul@183 | 225 | || `NOTIFY_SPACE_AVAILABLE` || Space available for writing || |
paul@183 | 226 | |
paul@457 | 227 | The delivery of notifications is requested by subscribing to notifications for |
paul@457 | 228 | a given resource via a notifier object: |
paul@457 | 229 | |
paul@457 | 230 | {{{ |
paul@575 | 231 | long client_subscribe(file_t *file, notify_flags_t flags, notifier_t *notifier); |
paul@457 | 232 | }}} |
paul@457 | 233 | |
paul@457 | 234 | A notifier object can be common throughout all threads in a task, being |
paul@457 | 235 | obtained using the following function: |
paul@457 | 236 | |
paul@457 | 237 | {{{ |
paul@575 | 238 | notifier_t *client_notifier_task(); |
paul@457 | 239 | }}} |
paul@457 | 240 | |
paul@457 | 241 | Alternatively, a local notifier can be created for use within a thread: |
paul@457 | 242 | |
paul@457 | 243 | {{{ |
paul@575 | 244 | notifier_t *client_notifier_local(); |
paul@457 | 245 | }}} |
paul@457 | 246 | |
paul@457 | 247 | Local notifiers must be closed when they are no longer needed: |
paul@457 | 248 | |
paul@457 | 249 | {{{ |
paul@575 | 250 | void client_notifier_close(notifier_t *notifier); |
paul@457 | 251 | }}} |
paul@457 | 252 | |
paul@457 | 253 | When notifications are no longer needed, an unsubscribe operation can be |
paul@457 | 254 | invoked: |
paul@457 | 255 | |
paul@457 | 256 | {{{ |
paul@575 | 257 | long client_unsubscribe(file_t *file, notifier_t *notifier); |
paul@457 | 258 | }}} |
paul@457 | 259 | |
paul@457 | 260 | ==== Example ==== |
paul@457 | 261 | |
paul@457 | 262 | {{{ |
paul@575 | 263 | notifier_t *notifier = client_notifier_local(); |
paul@457 | 264 | file_t *directory = client_open(filename, O_DIRECTORY); |
paul@457 | 265 | |
paul@457 | 266 | if (client_opened(directory)) |
paul@457 | 267 | { |
paul@457 | 268 | if (!client_subscribe(directory, NOTIFY_FILE_OPENED, notifier)) |
paul@457 | 269 | { |
paul@457 | 270 | if (!client_wait_file(directory, notifier)) |
paul@457 | 271 | { |
paul@457 | 272 | /* File opened in directory. */ |
paul@457 | 273 | } |
paul@457 | 274 | } |
paul@457 | 275 | } |
paul@457 | 276 | |
paul@457 | 277 | client_close(directory); |
paul@457 | 278 | client_notifier_close(notifier); |
paul@457 | 279 | }}} |
paul@457 | 280 | |
paul@183 | 281 | === Blocking Operations === |
paul@183 | 282 | |
paul@183 | 283 | Reading and writing operations can be configured to block if data cannot be |
paul@183 | 284 | read or written respectively. The following function is provided for this |
paul@183 | 285 | purpose: |
paul@183 | 286 | |
paul@183 | 287 | {{{ |
paul@183 | 288 | long client_set_blocking(file_t *file, notify_flags_t flags); |
paul@183 | 289 | }}} |
paul@183 | 290 | |
paul@183 | 291 | For pipes, blocking behaviour is the default and must be disabled explicitly, |
paul@183 | 292 | either by opening using the `O_NONBLOCK` flag or by calling |
paul@183 | 293 | `client_set_blocking` with no flags set. |
paul@457 | 294 | |
paul@457 | 295 | Blocking behaviour is supported using the notification functionality. When |
paul@457 | 296 | access to a file or pipe cannot be satisfied for a particular operation, such |
paul@457 | 297 | as reading not being able to yield more content or writing not being able to |
paul@457 | 298 | submit more content, the task-level notifier will be used to wait for |
paul@457 | 299 | notifications applicable to the file or pipe involved. Consequently, the |
paul@457 | 300 | access will effectively block until notifications are delivered indicating |
paul@457 | 301 | that the state of the file or pipe has changed, and until it is determined |
paul@457 | 302 | that the change of state will allow the operation to proceed successfully. |