Skip to content

Remove fooAbsolute functions from std.fs #16736

@squeek502

Description

@squeek502

This is a proposal regarding the Absolute suffixed functions in the std.fs namespace, see https://ziglang.org/documentation/master/std/#A;std:fs

First, here's the 'soft' version of this proposal:

Remove any fooAbsolute functions that are purely a wrapper around Dir.foo

Many of the fooAbsolute functions are purely wrappers around the relevant Dir function. For example, here's openDirAbsolute:

zig/lib/std/fs.zig

Lines 2711 to 2714 in c6e2e1a

pub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenDirOptions) File.OpenError!Dir {
assert(path.isAbsolute(absolute_path));
return cwd().openDir(absolute_path, flags);
}

The only thing these functions add are assertions that the path is absolute, but those can easily be done by the user at the callsite instead.

Note also that pretty much all Absolute functions have poor test coverage to the point that some will give a compile error if anyone tried to actually use them (e.g. on Windows anything that tries to call the no-longer-existing os.windows.cStrToWin32PrefixedFileW function [these functions have never worked since their introduction in #5879 which ended up removing cStrToWin32PrefixedFileW before it was merged but left some calls of it around])

I believe the above should be fairly uncontroversial.

Now, for the real proposal:

Remove all fooAbsolute functions from std.fs

This is something of a counter-proposal to #7540

This proposal has two parts:

  1. All Dir APIs should be able to handle both relative and absolute paths (this is already the case AFAIK)
  2. All non-Dir APIs should be removed in favor of Dir-based APIs

This is something that std.fs has been moving towards slowly, but it's never been formalized and so the current std.fs is a bit confused (and can be confusing for those new to Zig, since the most libc-like APIs are the fooAbsolute ones). Removing the Absolute APIs would allow Zig to communicate the design of the fs API more clearly and lead more people to write code/libraries that get the benefits of the Dir-based API for free.

Why Dir-based for everything?

My understanding of the reasoning for the Dir-based design is the following:

1. For "better general protection against Time Of Check, Time Of Use bugs across all Zig codebases"

From #3813, where things being more Dir-based was initiated:

It should now be clear the pattern that the standard library file system API is approaching. The API encourages programmers to take advantage of directory traversal via directory handles, for better general protection against Time Of Check, Time Of Use bugs across all Zig codebases.

Here's an example:

pub fn main() !void {
    var c_dir = try std.fs.cwd().makeOpenPath("a/b/c", .{});
    defer c_dir.close();

    // Imagine this happens from some other thread or process
    try std.fs.cwd().rename("a", "a_moved");

    // Because we use `c_dir` here, this will still work
    // and end up writing to the path `a_moved/b/c/some_file`
    try c_dir.writeFile("some_file", "hello");
}

With a non-Dir-based API, the above might instead be written as something like:

pub fn main() !void {
    try std.fs.makePath("a/b/c", .{});

    // Imagine this happens from some other thread or process
    try std.fs.rename("a", "a_moved");

    // This will now fail, since the `a` directory has
    // been moved but we assume it hasn't been.
    try std.fs.writeFile("a/b/c/some_file", "hello");
}

2. For better cross-platform compatibility by default

(my understanding is that this is mostly just a nice side-benefit, and wasn't originally a reason for the fs API design)

Platforms like WASI don't have the concept of an absolute path, so any API without an explicit Dir would exclude that code from being able to target WASI (or require a decent amount of compatibility code to infer a Dir from any given path when targeting WASI). From https://github.com/WebAssembly/wasi-filesystem:

WASI filesystems' APIs are passed a directory handle along with a path, and the path is looked up relative to the given handle, and sandboxed to be resolved within that directory

With the Zig filesystem API being designed to encourage the same thing (every filesystem function having an associated Dir), Zig code will be able to essentially target WASI by default without needing to do some of the contortions that something like wasi-libc has to do to support the 'normal' path-based APIs.

Note: WASI also doesn't have the concept of a cwd. Currently, std.fs.cwd() when targeting WASI will treat the first preopen as the cwd by default, but that behavior can be changed by supplying a custom wasiCwd function via a std_options struct in the root source file.


If the strong version of this proposal is accepted, then the only top-level functions in std.fs that would remain would be:

  • atomicSymLink, although it seems very possible this could/should be made into a Dir function
  • cwd
  • getAppDataDir
  • openSelfExe
  • rename/renameW/renameZ (these use Dirs)
  • selfExeDirPath/selfExeDirPathAlloc
  • selfExePath/selfExePathAlloc/selfExePathW

See #16743 for how removing the Absolute suffixed std.fs functions would affect the Zig codebase.

EDIT: In making the changes in that PR, I came across another reason why the Absolute versions aren't very useful: the Absolute terminology is overloaded and it's hard/impossible to tell why the Absolute variant is being used--is it being used just because the programmer knows the path happens to be absolute, or is it being used because the programmer knows the path must be absolute for the code to work correctly? Removing the Absolute variation will remove this ambiguity since the programmer will be forced to make their intentions more explicit if they want to assert/check that a given path is absolute.

Metadata

Metadata

Assignees

No one assigned

    Labels

    proposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.standard libraryThis issue involves writing Zig code for the standard library.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions