commit 53150ab548748e824ffd119661f997979049a779
parent b2814cd779dd321ae75538f3403a0c47a381ee15
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 8 Jan 2022 17:40:37 +0100
fs, os: add realpath
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
M | fs/fs.ha | | | 30 | ------------------------------ |
M | fs/util.ha | | | 66 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | os/fs.ha | | | 8 | ++++++++ |
3 files changed, 74 insertions(+), 30 deletions(-)
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -220,36 +220,6 @@ export fn rmdir(fs: *fs, path: str) (void | error) = {
};
};
-// Removes a directory, and anything in it.
-export fn rmdirall(fs: *fs, path: str) (void | error) = {
- let it = iter(fs, path)?;
- for (true) {
- match (next(it)) {
- case let ent: dirent =>
- if (ent.name == "." || ent.name == "..") {
- continue;
- };
- // XXX: This could probably be more efficient if we
- // re-used the buffer further down the call stack.
- let buf = alloc(path::init());
- defer free(buf);
- const path = path::set(buf, path, ent.name)!;
-
- switch (ent.ftype & mode::DIR) {
- case mode::DIR =>
- rmdirall(fs, path)?;
- case =>
- remove(fs, path)?;
- };
- case void =>
- break;
- };
- };
- if (path != "") {
- return rmdir(fs, path);
- };
-};
-
// Creates a directory and returns a subdir for it. Some filesystems support
// doing this operation atomically, but if not, a fallback is used.
export fn mksubdir(fs: *fs, path: str) (*fs | error) = {
diff --git a/fs/util.ha b/fs/util.ha
@@ -110,3 +110,69 @@ export fn dirents_free(d: []dirent) void = {
dirent_free(&d[i]);
};
};
+
+// Removes a directory, and anything in it.
+export fn rmdirall(fs: *fs, path: str) (void | error) = {
+ let it = iter(fs, path)?;
+ for (true) {
+ match (next(it)) {
+ case let ent: dirent =>
+ if (ent.name == "." || ent.name == "..") {
+ continue;
+ };
+ // XXX: This could probably be more efficient if we
+ // re-used the buffer further down the call stack.
+ let buf = alloc(path::init());
+ defer free(buf);
+ const path = path::set(buf, path, ent.name)!;
+
+ switch (ent.ftype & mode::DIR) {
+ case mode::DIR =>
+ rmdirall(fs, path)?;
+ case =>
+ remove(fs, path)?;
+ };
+ case void =>
+ break;
+ };
+ };
+ if (path != "") {
+ return rmdir(fs, path);
+ };
+};
+
+// Canonicalizes a path in this filesystem by resolving all symlinks and
+// collapsing any "." or ".." path components. The return value is statically
+// allocated and will be overwritten on subsequent calls.
+export fn realpath(fs: *fs, path: str) (str | error) = {
+ static let buf = path::buffer { ... };
+ path::reset(&buf);
+
+ const iter = path::iter(path);
+ for (true) {
+ const item = match (path::next(&iter)) {
+ case let item: str =>
+ yield item;
+ case void =>
+ break;
+ };
+
+ const item = path::add(&buf, item)!;
+ const link = match (fs::readlink(fs, item)) {
+ case let link: str =>
+ yield link;
+ case wrongtype =>
+ continue;
+ case let err: error =>
+ return err;
+ };
+
+ if (!path::abs(link)) {
+ path::add(&buf, "..", link)!;
+ } else {
+ path::set(&buf, link)!;
+ };
+ };
+
+ return path::string(&buf);
+};
diff --git a/os/fs.ha b/os/fs.ha
@@ -87,3 +87,11 @@ export fn create(
mode: fs::mode,
flags: fs::flags...
) (io::file | fs::error) = fs::create_file(cwd, path, mode, flags...);
+
+// Canonicalizes a path in this filesystem by resolving all symlinks and
+// collapsing any "." or ".." path components.
+//
+// This function is a thin shim over [[fs::resolve]], and the return value is
+// statically allocated by [[fs::resolve]]. Thus, calls to this function or to
+// [[fs::resolve]] will overwrite the return value of either function.
+export fn realpath(path: str) (str | fs::error) = fs::realpath(cwd, path);