Browse the docs

silk.system.filesystem_native.

The native provider of the FileSystem effect, confined to one directory of the host.

When to use#

Provide OsFileSystem for FileSystem in a native program. Code that uses files uses silk.system.filesystem, so a test can provide a file system in memory.

Details#

OsFileSystem.make takes a root directory of the host. The path / of the provider is that root, not the root of the host. A path cannot leave the root: .. is not valid in a Path, and the native provider resolves a symbolic link below the root inside the root. An absolute link target starts again at the root, and .. in a target never goes above it.

The write operations are different:

  • writeFile creates or truncates the file, then writes. If it fails, a partial file can stay.

  • createFile fails with AlreadyExists if the entry exists. If a later write fails, the new partial file stays.

  • writeFileAtomic writes a temporary file in the same directory, then renames it to the path (to replace) or links it (to create). A reader sees the old file or the complete new file, never a part. A replace changes a final symbolic link itself, not its target.

New files get mode 0666, filtered by the umask of the process. An atomic replace makes a new file, so the permissions, owner, extended attributes, and hard links of the old file are not kept. A synchronized atomic write calls fsync on the file before the rename, and on the directory after it. syncDirectory also uses fsync. These do not use F_FULLFSYNC, and do not promise that data survives a power loss.

On WASI, roots name preopened guest directories. The host controls rights, file permissions, and synchronization support.

Directory listings are sorted by the bytes of the paths. A native error becomes a FileError with a portable reason and the native code.

Errors#

  • FileError for each failed operation. If a close fails after an earlier error, the earlier error is returned.

  • AtomicWriteError from writeFileAtomic. It tells if the failure was before or after the new contents became visible, and names a temporary file that cleanup could not remove.

  • OutOfMemoryError from operations that return owned bytes or paths.

Gotchas#

  • Trust the root and its parent directories. The confinement does not protect against a process that renames those directories, changes mounts, or changes the temporary files.

  • After writeFileAtomic fails with a published error, the new contents are visible. Do not write again without a check.

  • The provider exists on WASI, Darwin on aarch64, and Linux with GNU libc on x86_64 or aarch64.

Examples#

Write and read a file in a temporary directory#

silk
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.collections.bytes { Bytes }
import silk.core.effect { Effect }
import silk.system.filesystem { FileError, FileSystem, Path }
import silk.system.filesystem_native { OsFileSystem }

effect fn program() -> i32
  ! FileError | OutOfMemoryError
  ? &mut FileSystem | &mut Allocator {
  // `/` is the root of the provider: /tmp on the host.
  let root = run Path.make("/")
  let scratch = run FileSystem.temporaryDirectory(&root, "silk-doc-")
  let file = run Path.join(&scratch.path, "note.txt")
  run FileSystem.writeFile(&file, b"hello")
  let bytes = run FileSystem.readFile(&file)
  let read = Bytes.asSlice(&bytes)
  let same = read.length == 5 && read[0] == 104
  drop read

  // Remove the directory and the file in it.
  run FileSystem.release(move scratch)
  if !same {
    return 1
  }
  return 0
}

effect fn failed<E>(error: E) -> i32 {
  drop error
  return 1
}

effect fn withFiles() -> i32
  ! FileError | OutOfMemoryError
  ? &mut Allocator {
  let mut files = run OsFileSystem.make("/tmp")
  return run program() |> Effect.provideMut<FileSystem>(&mut files)
}

pub fn main() -> i32 {
  let mut allocator = Allocator.systemAllocatorProvider()
  return run Effect.catchAll(withFiles(), failed) |> Effect.provideMut<Allocator>(&mut allocator)
}

Import as OsFileSystem with import silk.system.filesystem_native { OsFileSystem }.

Public declarations: 1.

OsFileSystem#

silk
pub struct OsFileSystem

The native FileSystem provider. It owns the bytes of its root directory.

Details#

Each Path is resolved below the root. The root is never shown as a Path. An operation that would leave the root fails. The path / of the provider is the root, not the root of the host. The native provider resolves symbolic links below the root inside the root. New files get mode 0666, filtered by the umask of the process. Directory listings are sorted by the bytes of the paths. A native error becomes a FileError with a portable reason and the native code.

On WASI, the root names a preopened guest directory. The host controls rights, permissions, and synchronization support.

Gotchas#

  • Trust the root and its parent directories. The confinement does not protect against a process that renames those directories, changes mounts, or changes the temporary files of writeFileAtomic.

  • writeFile and createFile can leave a partial file when a write fails. Use writeFileAtomic when a reader must see the old file or the complete new file.

  • Synchronized writes use fsync, not F_FULLFSYNC, and do not promise that data survives a power loss.

  • The provider exists on WASI, Darwin on aarch64, and Linux with GNU libc on x86_64 or aarch64.

Implementation OsFileSystem#

silk
impl OsFileSystem

Implementation OsFileSystem#

silk
impl OsFileSystem

Associated function make#

silk
pub effect fn make(root: string) -> OsFileSystem ! OutOfMemoryError ? &mut Allocator

Returns a provider with a copy of the text root as its root directory, such as "/" or "/tmp". It does not open the directory.

Errors#

OutOfMemoryError if the copy cannot be allocated.

Gotchas#

root must be an absolute host path, not empty, with no NUL. Other values trap.

Associated function makeBytes#

silk
pub effect fn makeBytes(root: &[u8]) -> OsFileSystem ! OutOfMemoryError ? &mut Allocator

Returns a provider with a copy of root as its root directory. It does not open the directory. The bytes do not have to be valid UTF-8.

When to use#

Use it for a root that you got from the host as bytes, such as the working directory from HostInput.workingDirectory.

Errors#

OutOfMemoryError if the copy cannot be allocated.

Gotchas#

root must be an absolute host path, not empty, with no NUL. Other values trap.

Implementation FileSystem for OsFileSystem#

silk
impl FileSystem for OsFileSystem

Operation readFile#

silk
readFile: OsFileSystem.readFile

Operation writeFile#

silk
writeFile: OsFileSystem.writeFile

Operation createFile#

silk
createFile: OsFileSystem.createFile

Operation writeFileAtomic#

silk
writeFileAtomic: OsFileSystem.writeFileAtomic

Operation syncDirectory#

silk
syncDirectory: OsFileSystem.syncDirectory

Operation stat#

silk
stat: OsFileSystem.stat

Operation listDirectory#

silk
listDirectory: OsFileSystem.listDirectory

Operation createDirectory#

silk
createDirectory: OsFileSystem.createDirectory

Operation removeFile#

silk
removeFile: OsFileSystem.removeFile

Operation removeDirectory#

silk
removeDirectory: OsFileSystem.removeDirectory

Operation createTemporaryDirectory#

silk
createTemporaryDirectory: OsFileSystem.createTemporaryDirectory

Category: System.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, x86_64-unknown-linux-gnu, wasm32-wasip1, wasm32-wasip2.

View the source of this page