hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 43375866f18b7576e3368039af7edaa7617f0e13
parent 51543047bdb9dcf7a8206a5eeb0c1e0e3cae1fae
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue,  9 Mar 2021 14:40:03 -0500

fs, os: unify open & create semantics

Diffstat:
Mfs/fs.ha | 22+++++++++++++++++-----
Mfs/types.ha | 35+++++++++++++++++++++++++++++++++--
Mhare/module/scan.ha | 2+-
Mos/+linux/dirfdfs.ha | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mos/+linux/open.ha | 102+++++++++++--------------------------------------------------------------------
5 files changed, 119 insertions(+), 114 deletions(-)

diff --git a/fs/fs.ha b/fs/fs.ha @@ -10,18 +10,30 @@ export fn close(fs: *fs) void = { }; // Opens a file. -export fn open(fs: *fs, path: path::path, mode: io::mode) (*io::stream | error) = { +// +// If no flags are provided, the default read/write mode is RDONLY. +export fn open( + fs: *fs, + path: path::path, + flags: flags... +) (*io::stream | error) = { return match (fs.open) { null => io::unsupported, - f: *openfunc => f(fs, path, mode), + f: *openfunc => f(fs, path, flags...), }; }; -// Creates a new file. The default file permissions are implementation defined. -export fn create(fs: *fs, path: path::path, mode: io::mode) (*io::stream | error) = { +// Equivalent to [open], but allows the caller to specify a file mode for the +// new file. If no other flag is given, the default read/write mode is WRONLY. +export fn create( + fs: *fs, + path: path::path, + mode: uint, + flags: flags... +) (*io::stream | error) = { return match (fs.create) { null => io::unsupported, - f: *createfunc => f(fs, path, mode), + f: *createfunc => f(fs, path, mode, flags...), }; }; diff --git a/fs/types.ha b/fs/types.ha @@ -139,9 +139,27 @@ export fn dirent_free(e: dirent) void = match (e.name) { b: []u8 => free(b), }; +// Flags to use for opening a file. Not all operating systems support all flags; +// at a minimum, RDONLY, WRONLY, RDWR, and CREATE will be supported. +export type flags = enum int { + RDONLY = 0, + WRONLY = 1, + RDWR = 2, + CREATE = 0o100, + EXCLUSIVE = 0o200, + NOCTTY = 0o400, + TRUNC = 0o1000, + APPEND = 0o2000, + NONBLOCK = 0o4000, + DSYNC = 0o10000, + SYNC = 0o4010000, + RSYNC = 0o4010000, + DIRECTORY = 0o200000, + NOFOLLOW = 0o400000, + CLOEXEC = 0o2000000, +}; + export type closefunc = fn(fs: *fs) void; -export type openfunc = fn(fs: *fs, path: path::path, mode: io::mode) (*io::stream | error); -export type createfunc = fn(fs: *fs, path: path::path, mode: io::mode) (*io::stream | error); export type iterfunc = fn(fs: *fs, path: path::path) (*iterator | error); export type statfunc = fn(fs: *fs, path: path::path) (filestat | error); export type subdirfunc = fn(fs: *fs, path: path::path) (*fs | error); @@ -149,6 +167,19 @@ export type mkdirfunc = fn(fs: *fs, path: path::path) (void | error); export type mksubdirfunc = fn(fs: *fs, path: path::path) (*fs | error); export type resolvefunc = fn(fs: *fs, path: path::path) path::path; +export type openfunc = fn( + fs: *fs, + path: path::path, + flags: flags... +) (*io::stream | error); + +export type createfunc = fn( + fs: *fs, + path: path::path, + mode: uint, + flags: flags... +) (*io::stream | error); + // An abstract implementation of a filesystem. To create a custom stream, embed // this type as the first member of a struct with user-specific data and fill // out these fields as appropriate. diff --git a/hare/module/scan.ha b/hare/module/scan.ha @@ -167,7 +167,7 @@ fn scan_file( path: path::path, deps: *[]ast::ident, ) ([]u8 | error) = { - let f = fs::open(ctx.fs, path, io::mode::READ)?; + let f = fs::open(ctx.fs, path)?; defer io::close(f); let sha = sha256::sha256(); //defer! hash::close(sha); diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha @@ -60,6 +60,11 @@ fn static_dirfdopen(fd: int, filesystem: *os_filesystem) *fs::fs = { // Opens a file descriptor as an [fs::fs]. This file descriptor must be a // directory file. The file will be closed when the fs is closed. +// +// If no other flags are provided to [fs::open] and [fs::create] when used with +// a dirfdfs, [fs::flags::NOCTTY] and [fs::flags::CLOEXEC] are used when opening +// the file. If you pass your own flags, it is recommended that you add these +// unless you know that you do not want them. export fn dirfdopen(fd: int, resolve: resolve...) *fs::fs = { let ofs = alloc(os_filesystem { ... }); let fs = static_dirfdopen(fd, ofs); @@ -118,38 +123,69 @@ fn _fs_open( return fdopen(fd, name, mode); }; -fn fs_open(fs: *fs::fs, path: path::path, mode: io::mode) (*io::stream | fs::error) = { - let oflags = rt::O_NOCTTY | rt::O_CLOEXEC; - if (mode & io::mode::RDWR == io::mode::RDWR) { - oflags |= rt::O_RDWR; - } else if (mode & io::mode::READ == io::mode::READ) { - oflags |= rt::O_RDONLY; - } else if (mode & io::mode::WRITE == io::mode::WRITE) { - oflags |= rt::O_WRONLY; +fn fs_open( + fs: *fs::fs, + path: path::path, + flags: fs::flags... +) (*io::stream | fs::error) = { + let oflags = 0; + let iomode = io::mode::NONE; + if (len(flags) == 0z) { + oflags |= (fs::flags::NOCTTY + | fs::flags::CLOEXEC + | fs::flags::RDONLY): int; + }; + for (let i = 0z; i < len(flags); i += 1z) { + oflags |= flags[i]: int; + }; + + if (oflags: fs::flags & fs::flags::RDWR == fs::flags::RDWR) { + iomode = io::mode::RDWR; + } else if (oflags: fs::flags & fs::flags::RDONLY == fs::flags::RDONLY) { + iomode = io::mode::READ; + } else if (oflags: fs::flags & fs::flags::RDONLY == fs::flags::RDONLY) { + iomode = io::mode::WRITE; }; let oh = rt::open_how { flags = oflags: u64, ... }; - return _fs_open(fs, path, mode, &oh); + return _fs_open(fs, path, iomode, &oh); }; -fn fs_create(fs: *fs::fs, path: path::path, mode: io::mode) (*io::stream | fs::error) = { - let oflags = rt::O_NOCTTY | rt::O_CLOEXEC | rt::O_CREATE; - if (mode & io::mode::RDWR == io::mode::RDWR) { - oflags |= rt::O_RDWR; - } else if (mode & io::mode::READ == io::mode::READ) { - oflags |= rt::O_RDONLY; - } else if (mode & io::mode::WRITE == io::mode::WRITE) { - oflags |= rt::O_WRONLY; +fn fs_create( + fs: *fs::fs, + path: path::path, + mode: uint, + flags: fs::flags... +) (*io::stream | fs::error) = { + let oflags = 0; + let iomode = io::mode::NONE; + if (len(flags) == 0z) { + oflags |= (fs::flags::NOCTTY + | fs::flags::CLOEXEC + | fs::flags::WRONLY): int; + }; + for (let i = 0z; i < len(flags); i += 1z) { + oflags |= flags[i]: int; + }; + oflags |= fs::flags::CREATE: int; + + if (oflags: fs::flags & fs::flags::RDWR == fs::flags::RDWR) { + iomode = io::mode::RDWR; + } else if (oflags: fs::flags & fs::flags::RDONLY == fs::flags::RDONLY) { + iomode = io::mode::READ; + } else if (oflags: fs::flags & fs::flags::RDONLY == fs::flags::RDONLY) { + iomode = io::mode::WRITE; }; let oh = rt::open_how { flags = oflags: u64, + mode = mode: u64, ... }; - return _fs_open(fs, path, mode, &oh); + return _fs_open(fs, path, iomode, &oh); }; fn fs_stat(fs: *fs::fs, path: path::path) (fs::filestat | fs::error) = { diff --git a/os/+linux/open.ha b/os/+linux/open.ha @@ -1,100 +1,26 @@ use fs; use io; use path; -use rt; -use strings; - -// Flags to use for opening a file. Not all operating systems support all flags; -// at a minimum, RDONLY, WRONLY, RDWR, and CREATE will be supported. -export type flags = enum int { - RDONLY = 0, - WRONLY = 1, - RDWR = 2, - CREATE = 0o100, - EXCLUSIVE = 0o200, - NOCTTY = 0o400, - TRUNC = 0o1000, - APPEND = 0o2000, - NONBLOCK = 0o4000, - DSYNC = 0o10000, - SYNC = 0o4010000, - RSYNC = 0o4010000, - DIRECTORY = 0o200000, - NOFOLLOW = 0o400000, - CLOEXEC = 0o2000000, -}; // Opens a file from the filesystem. // -// If no flags are provided, the default for +linux is RDONLY | NOCTTY | CLOEXEC. +// If no flags are provided, [fs::flags::RDONLY], [fs::flags::NOCTTY], +// [fs::flags::CLOEXEC] are used when opening the file. If you pass your own +// flags, it is recommended that you add the latter two unless you know that you +// do not want them. export fn open( path: path::path, - flag: flags... -) (*io::stream | io::error) = { - const name: str = match (path) { - s: str => s, - b: []u8 => "<open([]u8)>", - }; - - let oflags = 0; - let iomode = io::mode::NONE; - if (len(flag) == 0z) { - oflags |= (flags::NOCTTY | flags::CLOEXEC | flags::RDONLY): int; - }; - for (let i = 0z; i < len(flag); i += 1z) { - oflags |= flag[i]: int; - }; - - if (oflags: flags & flags::RDWR == flags::RDWR) { - iomode = io::mode::RDWR; - } else if (oflags: flags & flags::RDONLY == flags::RDONLY) { - iomode = io::mode::READ; - } else if (oflags: flags & flags::RDONLY == flags::RDONLY) { - iomode = io::mode::WRITE; - }; + flags: fs::flags... +) (*io::stream | fs::error) = fs::open(cwd, path, flags...); - let fd: int = match (rt::open(path, oflags, 0u)) { - err: rt::errno => return errno_to_io(err), - n: int => n, - }; - - return fdopen(fd, name, iomode); -}; - -// Equivalent to [open], but allows the caller to specify a file mode for the -// new file. If no other flag is given, the default read/write mode is WRONLY. +// Opens a file from the filesystem. +// +// If no flags are provided, [fs::flags::WRONLY], [fs::flags::NOCTTY], +// [fs::flags::CLOEXEC] are used when opening the file. If you pass your own +// flags, it is recommended that you add the latter two unless you know that you +// do not want them. export fn create( path: path::path, mode: uint, - flag: flags... -) (*io::stream | io::error) = { - const name: str = match (path) { - s: str => s, - b: []u8 => "<open([]u8)>", - }; - - let oflags = 0; - let iomode = io::mode::NONE; - if (len(flag) == 0z) { - oflags |= (flags::NOCTTY | flags::CLOEXEC | flags::WRONLY): int; - }; - for (let i = 0z; i < len(flag); i += 1z) { - oflags |= flag[i]: int; - }; - oflags |= flags::CREATE: int; - - if (oflags: flags & flags::RDWR == flags::RDWR) { - iomode = io::mode::RDWR; - } else if (oflags: flags & flags::RDONLY == flags::RDONLY) { - iomode = io::mode::READ; - } else if (oflags: flags & flags::RDONLY == flags::RDONLY) { - iomode = io::mode::WRITE; - }; - - let fd: int = match (rt::open(path, oflags, mode)) { - err: rt::errno => return errno_to_io(err), - n: int => n, - }; - - return fdopen(fd, name, iomode); -}; + flags: fs::flags... +) (*io::stream | fs::error) = fs::create(cwd, path, mode, flags...);