Does a file exist for Read Write?

Hi

There does not appear to be a “Does this file exist?” in the STL. Perhaps I missed it.

So I wrote this. Am I going the right way here? It seems like an awfully lot of plumbing for such a simple question.

Please feel free to criticizes. It’s the only way I will learn

Also Am I spacing this right? Seems like a waste to add 4 spaces on each line.

Thanks in advance

const std = @import("std");

    pub fn isFileRWExist( fn_dir:std.fs.Dir, fn_file_name:[]const u8) bool {

        // get allocator to hold dir
        var buf: [4096]u8 = undefined; // ext4 max directory path is 4096
        var fba = std.heap.FixedBufferAllocator.init(&buf);
        const allocator = fba.allocator();
        defer allocator.free(&buf);

        const dir_path=fn_dir.realpathAlloc(allocator, ".") catch |err| {
            switch (err) {
                else => {std.debug.print("!! Dir error \"{}\" \n",.{err}); std.process.exit(1); }, }
            };

        var file1_handle=fn_dir.openFile(fn_file_name,.{.mode=.read_write, .lock=.none}) catch |err| {
            switch (err) {
                error.FileNotFound => {return false;},
                else => {std.debug.print("!! File error \"{}\" on file {s} Dir {s}\n",.{err,fn_file_name, dir_path});
                        std.process.exit(1);
                        },
                }
            };
        defer file1_handle.close();

        return true;
    }



pub fn main() !void {

    const file_exists=isFileRWExist(std.fs.cwd(),"test.dat"); //_=file_exists;
    std.debug.print("Does the file exist = {}\n",.{file_exists});

} // end of main

1 Like

I think what you are looking for is this : std.posix.access, You can give it a path, and a flag X_OK for executable, F_OK for file and maybe dir I don’t recal, and W_OK and R_OK for read/write access.

2 Likes

as for the critique, I think that taking an allocator, even if this is just fixed allocation might be better, or even directly taking a write buffer for the path alloc, as initializing an allocator each time might be more overhead than necessary, also exiting from a function, is a bit harsh, you should return an error instead in my opinion. :slight_smile:

1 Like

I’ve written a example with std.fs.Dir.access.

pub fn isFileRWExist( fn_dir:std.fs.Dir, fn_file_name:[]const u8) !bool {
    fn_dir.access(fn_file_name, .{.mode = .read_write}) catch |err| switch (err) {
        error.FileNotFound => return false,
        error.PermissionDenied => return false,
        else => {
            // (snip)
            return err;
        }
    };

    return true;
}

pub fn main() !void {
    std.debug.print("foo.txt: {}\n", .{try isFileRWExist(std.fs.cwd(), "foo.txt")});
    std.debug.print("bar.txt: {}\n", .{try isFileRWExist(std.fs.cwd(), "bar.txt")});
    std.debug.print("foo.txt: {}\n", .{try isFileRWExist(std.fs.cwd(), "build.zig")});
}

result:

> touch bar.txt
> chmod 0444 bar.txt
> zig build run
foo.txt: false
bar.txt: false
foo.txt: true
2 Likes

Note that in general it is better to just do the io operation instead of checking first if it is valid. Even if the file exists, you might not have permission for the file or the file might disappear after the check and before the action.

So instead of checking if a file exists, just try to open it and then handle all errors.

9 Likes

Initializing the allocator is dirt cheap, and it probably won’t even exist in the final binary. The vtable is comptime-known and the compiler can easily emebed the address of the buffer, making the allocator disappear. It’s much better to do this than to take an allocator from outside.
Still, the best optin here would be realpath.

1 Like

You probably just want to use zig fmt

2 Likes

I want to highlight this reply - this is super important, and a lot of people haven’t learned this lesson yet. Please take this information to heart!

Looks like I did remember to mention this in the doc comments for std.fs.Dir.access :slight_smile:

2 Likes

Thank you ever so much Ktz_alias. I really appreciate your code.

I think you have answer my question "Am I going the right way here? ". Err, no I am not.

But how did you do it? I am not asking how your code works. I am asking how you knew about the method .access? What was your process? Did you read about it from some book, Which I must buy? Did you see an example on a website, that I must visit. Or did you simply bite the bullet and read the source code from scratch? How? HOW?

Yes, the method “access” in the stl for all to see. Yes it is the FIRST method in std.fs.Dir. And yes I must have skipped it a 1000 times, while looking to solve this problem. Yet I still look at the access entry (Below) and think “WTF8_does_this_do?”. What was the clue? Asking for someone who clearly doesn’t one.

All the best

STL entry

pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void

Test accessing sub_path. On Windows, sub_path should be encoded as WTF-8. On WASI, sub_path should be encoded as valid UTF-8. On other platforms, sub_path is an opaque sequence of bytes with no particular encoding. Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function. For example, instead of testing if a file exists and then opening it, just open it and handle the error for file not found.

The name comes from POSIX access which has the same purpose: access

Note that it includes the same warning:

Use of these functions is discouraged since by the time the returned information is acted upon, it is out-of-date. (That is, acting upon the information always leads to a time-of-check-to-time-of-use race condition.) An application should instead attempt the action itself and handle the [EACCES] error that occurs if the file is not accessible

1 Like

To answer your question, access, is a function that’s used a lot in C, Recently I’ve worked on a bash implementation in C, and access is used to check if files do exist before your redirect them, and you also use access to check that a path to a cmd, is indeed an executable command. So my tip for you is that if you want to know how to do something in Zig or if Zig does have a particular function, just google “how to do x in C ?” and then try to guess where that X equivalent is in the std by looking at the standard library doc in ziglang, Zig as almost always a 1:1 function to the C std, it’s obviously different, but there’s always some form of equivalent

1 Like