@@ -21,7 +21,6 @@ import okio.Path.Companion.toPath
21
21
import okio.internal.ErrnoException
22
22
import okio.internal.fdClose
23
23
import okio.internal.preview1.Errno
24
- import okio.internal.preview1.FirstPreopenDirectoryTmp
25
24
import okio.internal.preview1.dirnamelen
26
25
import okio.internal.preview1.fd
27
26
import okio.internal.preview1.fd_readdir
@@ -60,7 +59,18 @@ import okio.internal.write
60
59
*
61
60
* [WASI]: https://wasi.dev/
62
61
*/
63
- object WasiFileSystem : FileSystem() {
62
+ class WasiFileSystem (
63
+ private val relativePathPreopen : Int = DEFAULT_FIRST_PREOPEN ,
64
+ pathToPreopen : Map <Path , Int > = mapOf("/".toPath() to DEFAULT_FIRST_PREOPEN ),
65
+ ) : FileSystem() {
66
+ private val pathSegmentsToPreopen = pathToPreopen.mapKeys { (key, _) -> key.segmentsBytes }
67
+
68
+ init {
69
+ require(pathSegmentsToPreopen.isNotEmpty()) {
70
+ " pathToPreopen must be non-empty"
71
+ }
72
+ }
73
+
64
74
override fun canonicalize (path : Path ): Path {
65
75
// There's no APIs in preview1 to canonicalize a path. We give it a best effort by resolving
66
76
// all symlinks, but this could result in a relative path.
@@ -108,7 +118,7 @@ object WasiFileSystem : FileSystem() {
108
118
val (pathAddress, pathSize) = allocator.write(path.toString())
109
119
110
120
val errno = path_filestat_get(
111
- fd = FirstPreopenDirectoryTmp ,
121
+ fd = preopenFd(path) ? : return null ,
112
122
flags = 0 ,
113
123
path = pathAddress.address.toInt(),
114
124
pathSize = pathSize,
@@ -144,7 +154,7 @@ object WasiFileSystem : FileSystem() {
144
154
val bufPointer = allocator.allocate(bufLen)
145
155
val readlinkReturnPointer = allocator.allocate(4 ) // `size` is u32, 4 bytes.
146
156
val readlinkErrno = path_readlink(
147
- fd = FirstPreopenDirectoryTmp ,
157
+ fd = preopenFd(path) ? : return null ,
148
158
path = pathAddress.address.toInt(),
149
159
pathSize = pathSize,
150
160
buf = bufPointer.address.toInt(),
@@ -174,7 +184,7 @@ object WasiFileSystem : FileSystem() {
174
184
175
185
override fun list (dir : Path ): List <Path > {
176
186
val fd = pathOpen(
177
- path = dir.toString() ,
187
+ path = dir,
178
188
oflags = oflag_directory,
179
189
rightsBase = right_fd_readdir,
180
190
)
@@ -252,7 +262,7 @@ object WasiFileSystem : FileSystem() {
252
262
right_fd_seek or
253
263
right_fd_sync
254
264
val fd = pathOpen(
255
- path = file.toString() ,
265
+ path = file,
256
266
oflags = 0 ,
257
267
rightsBase = rightsBase,
258
268
)
@@ -275,7 +285,7 @@ object WasiFileSystem : FileSystem() {
275
285
right_fd_sync or
276
286
right_fd_write
277
287
val fd = pathOpen(
278
- path = file.toString() ,
288
+ path = file,
279
289
oflags = oflags,
280
290
rightsBase = rightsBase,
281
291
)
@@ -285,7 +295,7 @@ object WasiFileSystem : FileSystem() {
285
295
override fun source (file : Path ): Source {
286
296
return FileSource (
287
297
fd = pathOpen(
288
- path = file.toString() ,
298
+ path = file,
289
299
oflags = 0 ,
290
300
rightsBase = right_fd_read,
291
301
),
@@ -300,7 +310,7 @@ object WasiFileSystem : FileSystem() {
300
310
301
311
return FileSink (
302
312
fd = pathOpen(
303
- path = file.toString() ,
313
+ path = file,
304
314
oflags = oflags,
305
315
rightsBase = right_fd_write or right_fd_sync,
306
316
),
@@ -315,7 +325,7 @@ object WasiFileSystem : FileSystem() {
315
325
316
326
return FileSink (
317
327
fd = pathOpen(
318
- path = file.toString() ,
328
+ path = file,
319
329
oflags = oflags,
320
330
rightsBase = right_fd_write,
321
331
fdflags = fdflags_append,
@@ -328,7 +338,7 @@ object WasiFileSystem : FileSystem() {
328
338
val (pathAddress, pathSize) = allocator.write(dir.toString())
329
339
330
340
val errno = path_create_directory(
331
- fd = FirstPreopenDirectoryTmp ,
341
+ fd = preopenFd(dir) ? : throw FileNotFoundException ( " no preopen: $dir " ) ,
332
342
path = pathAddress.address.toInt(),
333
343
pathSize = pathSize,
334
344
)
@@ -347,10 +357,10 @@ object WasiFileSystem : FileSystem() {
347
357
val (targetPathAddress, targetPathSize) = allocator.write(target.toString())
348
358
349
359
val errno = path_rename(
350
- fd = FirstPreopenDirectoryTmp ,
360
+ fd = preopenFd(source) ? : throw FileNotFoundException ( " no preopen: $source " ) ,
351
361
old_path = sourcePathAddress.address.toInt(),
352
362
old_pathSize = sourcePathSize,
353
- new_fd = FirstPreopenDirectoryTmp ,
363
+ new_fd = preopenFd(target) ? : throw FileNotFoundException ( " no preopen: $target " ) ,
354
364
new_path = targetPathAddress.address.toInt(),
355
365
new_pathSize = targetPathSize,
356
366
)
@@ -365,9 +375,10 @@ object WasiFileSystem : FileSystem() {
365
375
override fun delete (path : Path , mustExist : Boolean ) {
366
376
withScopedMemoryAllocator { allocator ->
367
377
val (pathAddress, pathSize) = allocator.write(path.toString())
378
+ val preopenFd = preopenFd(path) ? : throw FileNotFoundException (" no preopen: $path " )
368
379
369
380
var errno = path_unlink_file(
370
- fd = FirstPreopenDirectoryTmp ,
381
+ fd = preopenFd ,
371
382
path = pathAddress.address.toInt(),
372
383
pathSize = pathSize,
373
384
)
@@ -382,7 +393,7 @@ object WasiFileSystem : FileSystem() {
382
393
Errno .isdir.ordinal,
383
394
-> {
384
395
errno = path_remove_directory(
385
- fd = FirstPreopenDirectoryTmp ,
396
+ fd = preopenFd ,
386
397
path = pathAddress.address.toInt(),
387
398
pathSize = pathSize,
388
399
)
@@ -400,7 +411,7 @@ object WasiFileSystem : FileSystem() {
400
411
val errno = path_symlink(
401
412
old_path = targetPathAddress.address.toInt(),
402
413
old_pathSize = targetPathSize,
403
- fd = FirstPreopenDirectoryTmp ,
414
+ fd = preopenFd(source) ? : throw FileNotFoundException ( " no preopen: $source " ) ,
404
415
new_path = sourcePathAddress.address.toInt(),
405
416
new_pathSize = sourcePathSize,
406
417
)
@@ -409,17 +420,18 @@ object WasiFileSystem : FileSystem() {
409
420
}
410
421
411
422
private fun pathOpen (
412
- path : String ,
423
+ path : Path ,
413
424
oflags : oflags,
414
425
rightsBase : rights,
415
426
fdflags : fdflags = 0,
416
427
): fd {
417
428
withScopedMemoryAllocator { allocator ->
418
- val (pathAddress, pathSize) = allocator.write(path)
429
+ val preopenFd = preopenFd(path) ? : throw FileNotFoundException (" no preopen: $path " )
430
+ val (pathAddress, pathSize) = allocator.write(path.toString())
419
431
420
432
val returnPointer: Pointer = allocator.allocate(4 ) // fd is u32.
421
433
val errno = path_open(
422
- fd = FirstPreopenDirectoryTmp ,
434
+ fd = preopenFd ,
423
435
dirflags = 0 ,
424
436
path = pathAddress.address.toInt(),
425
437
pathSize = pathSize,
@@ -437,5 +449,31 @@ object WasiFileSystem : FileSystem() {
437
449
}
438
450
}
439
451
452
+ /* *
453
+ * Returns the file descriptor of the preopened path that is an ancestor of [path]. Returns null
454
+ * if there is no such file descriptor.
455
+ */
456
+ private fun preopenFd (path : Path ): fd? {
457
+ if (path.isRelative) return relativePathPreopen
458
+
459
+ val pathSegmentsBytes = path.segmentsBytes
460
+ for ((candidate, fd) in pathSegmentsToPreopen) {
461
+ if (pathSegmentsBytes.size < candidate.size) continue
462
+ if (pathSegmentsBytes.subList(0 , candidate.size) != candidate) continue
463
+ return fd
464
+ }
465
+ return null
466
+ }
467
+
440
468
override fun toString () = " okio.WasiFileSystem"
469
+
470
+ companion object {
471
+ /* *
472
+ * File descriptor of the first preopen in the `WASI` instance's configured `preopens` property.
473
+ * This is 3 by default, assuming `stdin` is 0, `stdout` is 1, and `stderr` is 2.
474
+ *
475
+ * Other preopens are assigned sequentially starting at this value.
476
+ */
477
+ val DEFAULT_FIRST_PREOPEN = 3
478
+ }
441
479
}
0 commit comments