Re: [osv-dev] [PATCH] unlinkat: fill the gaps in the implementation

2022-05-17 Thread Nadav Har'El
On Wed, May 18, 2022 at 6:26 AM Waldemar Kozaczuk 
wrote:

> This patch enhances the unlinkat() implementation to handle
> the AT_FDCWD dirfd and AT_REMOVEDIR flags.
>
> We also enhance tst-remove.cc to test unlinkat.
>
> Signed-off-by: Waldemar Kozaczuk 
> ---
>  fs/vfs/main.cc  | 18 ++
>  tests/tst-remove.cc | 18 --
>  2 files changed, 30 insertions(+), 6 deletions(-)
>
> diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
> index a72042b2..4f0ce463 100644
> --- a/fs/vfs/main.cc
> +++ b/fs/vfs/main.cc
> @@ -1154,11 +1154,21 @@ int unlink(const char *pathname)
>  OSV_LIBC_API
>  int unlinkat(int dirfd, const char *pathname, int flags)
>  {
> -//TODO: Really implement it
> -if (dirfd != AT_FDCWD || flags) {
> -UNIMPLEMENTED("unlinkat() with non-zero flags or dirfd !=
> AT_FDCWD");
> +if (pathname[0] == '/' || dirfd == AT_FDCWD) {
> +if (flags & AT_REMOVEDIR) {
> +return rmdir(pathname);
> +} else {
> +return unlink(pathname);
> +}
>  }
> -return unlink(pathname);
> +
> +return vfs_fun_at(dirfd, pathname, [flags](const char *absolute_path)
> {
> +if (flags & AT_REMOVEDIR) {
> +return rmdir(absolute_path);
> +} else {
> +return unlink(absolute_path);
> +}
> +});
>

In my review of my other patch, I suggested that the AT_FDCWD thing should
be part of vfs_fun_at().
After all you can clearly see that you have exactly the same lambda
function twice in this code, it's
almost begging to have just one copy of this lambda, and vfs_fun_at will
just call it in one of two ways.



>  }
>
>  TRACEPOINT(trace_vfs_stat, "\"%s\" %p", const char*, struct stat*);
> diff --git a/tests/tst-remove.cc b/tests/tst-remove.cc
> index 6851cba0..fdf4037d 100644
> --- a/tests/tst-remove.cc
> +++ b/tests/tst-remove.cc
> @@ -42,6 +42,8 @@ bool do_expect(T actual, T expected, const char
> *actuals, const char *expecteds,
>  int main(int argc, char **argv)
>  {
>  expect(mkdir("/tmp/tst-remove", 0777), 0);
> +auto tst_remove_dir = open("/tmp/tst-remove", O_DIRECTORY);
> +expect(tst_remove_dir != -1, true);
>
>  /* test unlink() **/
>  // unlink() non-existant file returns ENOENT
> @@ -79,12 +81,24 @@ int main(int argc, char **argv)
>  expect_errno(rmdir("/tmp/tst-remove/f"), ENOTDIR);
>  expect(unlink("/tmp/tst-remove/f"), 0);
>
> -/* test remove() ***/
> -// TODO...
> +/* test unlinkat() ***/
> +expect(mknod("/tmp/tst-remove/u", 0777|S_IFREG, 0), 0);
> +expect(unlinkat(tst_remove_dir, "u", 0), 0);
>
> +expect(mknod("/tmp/tst-remove/u2", 0777|S_IFREG, 0), 0);
> +expect(chdir("/tmp/tst-remove"), 0);
> +expect(unlinkat(AT_FDCWD, "u2", 0), 0);
> +
> +expect(mkdir("/tmp/tst-remove/ud", 0777), 0);
> +expect(unlinkat(tst_remove_dir, "ud", AT_REMOVEDIR), 0);
> +
> +expect(mkdir("/tmp/tst-remove/ud2", 0777), 0);
> +expect(chdir("/tmp/tst-remove"), 0);
> +expect(unlinkat(AT_FDCWD, "ud2", AT_REMOVEDIR), 0);
>
>  // Finally remove the temporary directory (assumes the above left
>  // nothing in it)
> +expect(close(tst_remove_dir), 0);
>  expect(rmdir("/tmp/tst-remove"), 0);
>
>
> --
> 2.34.1
>
> --
> You received this message because you are subscribed to the Google Groups
> "OSv Development" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to osv-dev+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/osv-dev/20220518032614.76774-1-jwkozaczuk%40gmail.com
> .
>

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/CANEVyjv2b9V_KH%3Dk2264-4%3D8815gUCKMP6eMHFxRwN%3D1AKHDmw%40mail.gmail.com.


Re: [osv-dev] [PATCH] vfs: refactor the *at() functions

2022-05-17 Thread Nadav Har'El
On Wed, May 18, 2022 at 6:25 AM Waldemar Kozaczuk 
wrote:

> The VFS functions like openat(), faccessat() and others alike
> take a directory descriptor argument intended to make a filesystem
> action happen relative to that directory.
>
> The implemetations of those function repeat almost the same code
> over and over. So this patch makes vfs more DRY by introducing
> a helper function - vfs_fun_at() - which implements common
> logic and executed a lambda function specific to given
> VFS action.
>
> This patch also adds some unit tests around readlinkat() and mkdirat().
>
> Signed-off-by: Waldemar Kozaczuk 
> ---
>  fs/vfs/main.cc   | 188 +++
>  tests/tst-readdir.cc |   9 +++
>  tests/tst-symlink.cc |  12 ++-
>  3 files changed, 66 insertions(+), 143 deletions(-)
>
> diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
> index ca357cc8..a72042b2 100644
> --- a/fs/vfs/main.cc
> +++ b/fs/vfs/main.cc
> @@ -160,21 +160,8 @@ int open(const char *pathname, int flags, ...)
>
>  LFS64(open);
>
> -OSV_LIBC_API
> -int openat(int dirfd, const char *pathname, int flags, ...)
> +static int vfs_fun_at(int dirfd, const char *pathname,
> std::function fun)
>

Nitpick: I think if you want zero runtime overhead for this function, you
can use a template
parameter for this "fun" instead of std::function. But I don't know, maybe
the compiler is actually
smart enough to optimize all this std::function stuff away when inlining
vfs_fun_at calls?
And it's probably negligible anyway compared to all those strlcat calls :-)

 {
> -mode_t mode = 0;
> -if (flags & O_CREAT) {
> -va_list ap;
> -va_start(ap, flags);
> -mode = apply_umask(va_arg(ap, mode_t));
> -va_end(ap);
> -}
> -
> -if (pathname[0] == '/' || dirfd == AT_FDCWD) {
> -return open(pathname, flags, mode);
> -}
>

Unless I'm misunderstanding something, I think this if() should remain (and
of
course call fun(), not open()). More on this below.

-
>  struct file *fp;
>  int error = fget(dirfd, &fp);
>  if (error) {
> @@ -191,16 +178,38 @@ int openat(int dirfd, const char *pathname, int
> flags, ...)
>  /* build absolute path */
>  strlcpy(p, fp->f_dentry->d_mount->m_path, PATH_MAX);
>  strlcat(p, fp->f_dentry->d_path, PATH_MAX);
> -strlcat(p, "/", PATH_MAX);
> -strlcat(p, pathname, PATH_MAX);
> +if (pathname) {
>

Is any *at() function actually allowed to pass a null pathname? (which is
not the same as an empty string).
If Linux allows this, we should have a test for it.

+strlcat(p, "/", PATH_MAX);
> +strlcat(p, pathname, PATH_MAX);
> +}
>
> -error = open(p, flags, mode);
> +error = fun(p);
>
>  vn_unlock(vp);
>  fdrop(fp);
>
>  return error;
>  }
> +
> +OSV_LIBC_API
> +int openat(int dirfd, const char *pathname, int flags, ...)
> +{
> +mode_t mode = 0;
> +if (flags & O_CREAT) {
> +va_list ap;
> +va_start(ap, flags);
> +mode = apply_umask(va_arg(ap, mode_t));
> +va_end(ap);
> +}
>
+
> +if (pathname[0] == '/' || dirfd == AT_FDCWD) {
> +return open(pathname, flags, mode);
> +}
>

I think this if() should be part of the general vfs_fun_at()  code.
I looked at openat(2), mkdirat(2), fdaccessat(2) for example - and they
all support AT_FDCWD. Are there functions which don't?

+
> +return vfs_fun_at(dirfd, pathname, [flags, mode](const char
> *absolute_path) {
> +return open(absolute_path, flags, mode);
> +});
> +}
>  LFS64(openat);
>
>  // open() has an optional third argument, "mode", which is only needed in
> @@ -611,35 +620,14 @@ int __fxstatat(int ver, int dirfd, const char
> *pathname, struct stat *st,
>  return fstat(dirfd, st);
>  }
>
> -struct file *fp;
> -int error = fget(dirfd, &fp);
> -if (error) {
> -errno = error;
> -return -1;
> -}
> -
> -struct vnode *vp = fp->f_dentry->d_vnode;
> -vn_lock(vp);
> -
> -std::unique_ptr up (new char[PATH_MAX]);
> -char *p = up.get();
> -/* build absolute path */
> -strlcpy(p, fp->f_dentry->d_mount->m_path, PATH_MAX);
> -strlcat(p, fp->f_dentry->d_path, PATH_MAX);
> -strlcat(p, "/", PATH_MAX);
> -strlcat(p, pathname, PATH_MAX);
> -
> -if (flags & AT_SYMLINK_NOFOLLOW) {
> -error = lstat(p, st);
> -}
> -else {
> -error = stat(p, st);
> -}
> -
> -vn_unlock(vp);
> -fdrop(fp);
> -
> -return error;
> +return vfs_fun_at(dirfd, pathname, [flags,st](const char
> *absolute_path) {
> +if (flags & AT_SYMLINK_NOFOLLOW) {
> +return lstat(absolute_path, st);
> +}
> +else {
> +return stat(absolute_path, st);
> +}
> +});
>  }
>
>  LFS64(__fxstatat);
> @@ -875,32 +863,9 @@ int mkdirat(int dirfd, const char *pathname, mode_t
> mode)
>  return mkdir(pathname, mode);
>  }
>
> -// Supplied path is relative to folder

[osv-dev] [PATCH] vfs: implement symlinkat

2022-05-17 Thread Waldemar Kozaczuk
This patch implements the symlinkat() function and enhances
tst-symlink.cc to unit test it.

Signed-off-by: Waldemar Kozaczuk 
---
 exported_symbols/osv_ld-musl.so.1.symbols |  1 +
 exported_symbols/osv_libc.so.6.symbols|  1 +
 fs/vfs/main.cc| 12 
 tests/tst-symlink.cc  | 18 +++---
 4 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/exported_symbols/osv_ld-musl.so.1.symbols 
b/exported_symbols/osv_ld-musl.so.1.symbols
index f1c61a3f..3db22e0d 100644
--- a/exported_symbols/osv_ld-musl.so.1.symbols
+++ b/exported_symbols/osv_ld-musl.so.1.symbols
@@ -1081,6 +1081,7 @@ swab
 swprintf
 swscanf
 symlink
+symlinkat
 sync
 syscall
 sysconf
diff --git a/exported_symbols/osv_libc.so.6.symbols 
b/exported_symbols/osv_libc.so.6.symbols
index 7ae57c38..e29059bb 100644
--- a/exported_symbols/osv_libc.so.6.symbols
+++ b/exported_symbols/osv_libc.so.6.symbols
@@ -887,6 +887,7 @@ swprintf
 __swprintf_chk
 swscanf
 symlink
+symlinkat
 sync
 syscall
 sysconf
diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
index 4f0ce463..1b0d7c11 100644
--- a/fs/vfs/main.cc
+++ b/fs/vfs/main.cc
@@ -1122,6 +1122,18 @@ int symlink(const char *oldpath, const char *newpath)
 return 0;
 }
 
+OSV_LIBC_API
+int symlinkat(const char *oldpath, int newdirfd, const char *newpath)
+{
+if (newpath[0] == '/' || newdirfd == AT_FDCWD) {
+return symlink(oldpath, newpath);
+}
+
+return vfs_fun_at(newdirfd, newpath, [oldpath](const char *absolute_path) {
+return symlink(oldpath, absolute_path);
+});
+}
+
 TRACEPOINT(trace_vfs_unlink, "\"%s\"", const char*);
 TRACEPOINT(trace_vfs_unlink_ret, "");
 TRACEPOINT(trace_vfs_unlink_err, "%d", int);
diff --git a/tests/tst-symlink.cc b/tests/tst-symlink.cc
index 978cfda3..1322e79e 100644
--- a/tests/tst-symlink.cc
+++ b/tests/tst-symlink.cc
@@ -25,6 +25,9 @@
 
 #define N1"f1"
 #define N2"f2_AAA"
+#define N2B   "f2_BBB"
+#define N2B   "f2_BBB"
+#define N2C   "f2_CCC"
 #define N3"f3"
 #define N4"f4"
 #define N5"f5"
@@ -91,6 +94,8 @@ int main(int argc, char **argv)
 #endif
 
 report(chdir(TESTDIR) == 0, "chdir");
+auto test_dir = opendir(TESTDIR);
+report(test_dir, "opendir");
 
 /*
  * test to check
@@ -115,6 +120,10 @@ int main(int argc, char **argv)
 #else
 report(symlink(N1, N2) == 0, "symlink");
 report(search_dir(TESTDIR, N2) == true, "search dir");
+report(symlinkat(N1, dirfd(test_dir), N2B) == 0, "symlinkat");
+report(search_dir(TESTDIR, N2B) == true, "search dir N2B");
+report(symlinkat(N1, AT_FDCWD, N2C) == 0, "symlinkat");
+report(search_dir(TESTDIR, N2C) == true, "search dir N2B");
 #endif
 
 #if defined(READ_ONLY_FS)
@@ -125,6 +134,8 @@ int main(int argc, char **argv)
 #else
 report(access(N1, R_OK | W_OK) == 0, "access");
 report(access(N2, R_OK | W_OK) == 0, "access");
+report(access(N2B, R_OK | W_OK) == 0, "access");
+report(access(N2C, R_OK | W_OK) == 0, "access");
 #endif
 
 rc = readlink(N2, path, sizeof(path));
@@ -157,6 +168,8 @@ int main(int argc, char **argv)
 error = errno;
 report(rc < 0 && errno == ENOENT, "ENOENT expected");
 report(unlink(N2) == 0, "unlink");
+report(unlinkat(dirfd(test_dir),N2B,0) == 0, "unlinkat");
+report(unlinkat(dirfd(test_dir),N2C,0) == 0, "unlinkat");
 
 /*
  * IO Tests 1: write(file), read(symlink), truncate(symlink)
@@ -365,8 +378,6 @@ int main(int argc, char **argv)
 report(search_dir(D2, N5) == true, "Symlink search");
 
 report(rename(D2, D3) == 0, "rename(d2, d3)");
-auto test_dir = opendir(TESTDIR);
-report(test_dir, "opendir");
 rc = readlinkat(dirfd(test_dir), D3, path, sizeof(path));
 report(rc >= 0, "readlinkat");
 path[rc] = 0;
@@ -381,7 +392,6 @@ int main(int argc, char **argv)
 report(rc >= 0, "readlinkat");
 path[rc] = 0;
 report(strcmp(path, D1) == 0, "readlinkat path");
-report(closedir(test_dir) == 0, "closedir(test_dir)");
 rc = readlink(D3, path, sizeof(path));
 report(rc >= 0, "readlink");
 path[rc] = 0;
@@ -399,6 +409,8 @@ int main(int argc, char **argv)
 report(rmdir(D4) == 0, "rmdir");
 #endif
 
+report(closedir(test_dir) == 0, "closedir(test_dir)");
+
 #if defined(READ_ONLY_FS)
 report(-1 == rmdir(TESTDIR) && errno == ENOTEMPTY, "rmdir");
 #else
-- 
2.34.1

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/20220518032648.76794-1-jwkozaczuk%40gmail.com.


[osv-dev] [PATCH] unlinkat: fill the gaps in the implementation

2022-05-17 Thread Waldemar Kozaczuk
This patch enhances the unlinkat() implementation to handle
the AT_FDCWD dirfd and AT_REMOVEDIR flags. 

We also enhance tst-remove.cc to test unlinkat.

Signed-off-by: Waldemar Kozaczuk 
---
 fs/vfs/main.cc  | 18 ++
 tests/tst-remove.cc | 18 --
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
index a72042b2..4f0ce463 100644
--- a/fs/vfs/main.cc
+++ b/fs/vfs/main.cc
@@ -1154,11 +1154,21 @@ int unlink(const char *pathname)
 OSV_LIBC_API
 int unlinkat(int dirfd, const char *pathname, int flags)
 {
-//TODO: Really implement it
-if (dirfd != AT_FDCWD || flags) {
-UNIMPLEMENTED("unlinkat() with non-zero flags or dirfd != AT_FDCWD");
+if (pathname[0] == '/' || dirfd == AT_FDCWD) {
+if (flags & AT_REMOVEDIR) {
+return rmdir(pathname);
+} else {
+return unlink(pathname);
+}
 }
-return unlink(pathname);
+
+return vfs_fun_at(dirfd, pathname, [flags](const char *absolute_path) {
+if (flags & AT_REMOVEDIR) {
+return rmdir(absolute_path);
+} else {
+return unlink(absolute_path);
+}
+});
 }
 
 TRACEPOINT(trace_vfs_stat, "\"%s\" %p", const char*, struct stat*);
diff --git a/tests/tst-remove.cc b/tests/tst-remove.cc
index 6851cba0..fdf4037d 100644
--- a/tests/tst-remove.cc
+++ b/tests/tst-remove.cc
@@ -42,6 +42,8 @@ bool do_expect(T actual, T expected, const char *actuals, 
const char *expecteds,
 int main(int argc, char **argv)
 {
 expect(mkdir("/tmp/tst-remove", 0777), 0);
+auto tst_remove_dir = open("/tmp/tst-remove", O_DIRECTORY);
+expect(tst_remove_dir != -1, true);
 
 /* test unlink() **/
 // unlink() non-existant file returns ENOENT
@@ -79,12 +81,24 @@ int main(int argc, char **argv)
 expect_errno(rmdir("/tmp/tst-remove/f"), ENOTDIR);
 expect(unlink("/tmp/tst-remove/f"), 0);
 
-/* test remove() ***/
-// TODO...
+/* test unlinkat() ***/
+expect(mknod("/tmp/tst-remove/u", 0777|S_IFREG, 0), 0);
+expect(unlinkat(tst_remove_dir, "u", 0), 0);
 
+expect(mknod("/tmp/tst-remove/u2", 0777|S_IFREG, 0), 0);
+expect(chdir("/tmp/tst-remove"), 0);
+expect(unlinkat(AT_FDCWD, "u2", 0), 0);
+
+expect(mkdir("/tmp/tst-remove/ud", 0777), 0);
+expect(unlinkat(tst_remove_dir, "ud", AT_REMOVEDIR), 0);
+
+expect(mkdir("/tmp/tst-remove/ud2", 0777), 0);
+expect(chdir("/tmp/tst-remove"), 0);
+expect(unlinkat(AT_FDCWD, "ud2", AT_REMOVEDIR), 0);
 
 // Finally remove the temporary directory (assumes the above left
 // nothing in it)
+expect(close(tst_remove_dir), 0);
 expect(rmdir("/tmp/tst-remove"), 0);
 
 
-- 
2.34.1

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/20220518032614.76774-1-jwkozaczuk%40gmail.com.


[osv-dev] [PATCH] vfs: refactor the *at() functions

2022-05-17 Thread Waldemar Kozaczuk
The VFS functions like openat(), faccessat() and others alike
take a directory descriptor argument intended to make a filesystem
action happen relative to that directory.

The implemetations of those function repeat almost the same code
over and over. So this patch makes vfs more DRY by introducing 
a helper function - vfs_fun_at() - which implements common
logic and executed a lambda function specific to given
VFS action.

This patch also adds some unit tests around readlinkat() and mkdirat().

Signed-off-by: Waldemar Kozaczuk 
---
 fs/vfs/main.cc   | 188 +++
 tests/tst-readdir.cc |   9 +++
 tests/tst-symlink.cc |  12 ++-
 3 files changed, 66 insertions(+), 143 deletions(-)

diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
index ca357cc8..a72042b2 100644
--- a/fs/vfs/main.cc
+++ b/fs/vfs/main.cc
@@ -160,21 +160,8 @@ int open(const char *pathname, int flags, ...)
 
 LFS64(open);
 
-OSV_LIBC_API
-int openat(int dirfd, const char *pathname, int flags, ...)
+static int vfs_fun_at(int dirfd, const char *pathname, std::function fun)
 {
-mode_t mode = 0;
-if (flags & O_CREAT) {
-va_list ap;
-va_start(ap, flags);
-mode = apply_umask(va_arg(ap, mode_t));
-va_end(ap);
-}
-
-if (pathname[0] == '/' || dirfd == AT_FDCWD) {
-return open(pathname, flags, mode);
-}
-
 struct file *fp;
 int error = fget(dirfd, &fp);
 if (error) {
@@ -191,16 +178,38 @@ int openat(int dirfd, const char *pathname, int flags, 
...)
 /* build absolute path */
 strlcpy(p, fp->f_dentry->d_mount->m_path, PATH_MAX);
 strlcat(p, fp->f_dentry->d_path, PATH_MAX);
-strlcat(p, "/", PATH_MAX);
-strlcat(p, pathname, PATH_MAX);
+if (pathname) {
+strlcat(p, "/", PATH_MAX);
+strlcat(p, pathname, PATH_MAX);
+}
 
-error = open(p, flags, mode);
+error = fun(p);
 
 vn_unlock(vp);
 fdrop(fp);
 
 return error;
 }
+
+OSV_LIBC_API
+int openat(int dirfd, const char *pathname, int flags, ...)
+{
+mode_t mode = 0;
+if (flags & O_CREAT) {
+va_list ap;
+va_start(ap, flags);
+mode = apply_umask(va_arg(ap, mode_t));
+va_end(ap);
+}
+
+if (pathname[0] == '/' || dirfd == AT_FDCWD) {
+return open(pathname, flags, mode);
+}
+
+return vfs_fun_at(dirfd, pathname, [flags, mode](const char 
*absolute_path) {
+return open(absolute_path, flags, mode);
+});
+}
 LFS64(openat);
 
 // open() has an optional third argument, "mode", which is only needed in
@@ -611,35 +620,14 @@ int __fxstatat(int ver, int dirfd, const char *pathname, 
struct stat *st,
 return fstat(dirfd, st);
 }
 
-struct file *fp;
-int error = fget(dirfd, &fp);
-if (error) {
-errno = error;
-return -1;
-}
-
-struct vnode *vp = fp->f_dentry->d_vnode;
-vn_lock(vp);
-
-std::unique_ptr up (new char[PATH_MAX]);
-char *p = up.get();
-/* build absolute path */
-strlcpy(p, fp->f_dentry->d_mount->m_path, PATH_MAX);
-strlcat(p, fp->f_dentry->d_path, PATH_MAX);
-strlcat(p, "/", PATH_MAX);
-strlcat(p, pathname, PATH_MAX);
-
-if (flags & AT_SYMLINK_NOFOLLOW) {
-error = lstat(p, st);
-}
-else {
-error = stat(p, st);
-}
-
-vn_unlock(vp);
-fdrop(fp);
-
-return error;
+return vfs_fun_at(dirfd, pathname, [flags,st](const char *absolute_path) {
+if (flags & AT_SYMLINK_NOFOLLOW) {
+return lstat(absolute_path, st);
+}
+else {
+return stat(absolute_path, st);
+}
+});
 }
 
 LFS64(__fxstatat);
@@ -875,32 +863,9 @@ int mkdirat(int dirfd, const char *pathname, mode_t mode)
 return mkdir(pathname, mode);
 }
 
-// Supplied path is relative to folder specified by dirfd
-struct file *fp;
-int error = fget(dirfd, &fp);
-if (error) {
-errno = error;
-return -1;
-}
-
-struct vnode *vp = fp->f_dentry->d_vnode;
-vn_lock(vp);
-
-std::unique_ptr up (new char[PATH_MAX]);
-char *p = up.get();
-
-/* build absolute path */
-strlcpy(p, fp->f_dentry->d_mount->m_path, PATH_MAX);
-strlcat(p, fp->f_dentry->d_path, PATH_MAX);
-strlcat(p, "/", PATH_MAX);
-strlcat(p, pathname, PATH_MAX);
-
-error = mkdir(p, mode);
-
-vn_unlock(vp);
-fdrop(fp);
-
-return error;
+return vfs_fun_at(dirfd, pathname, [mode](const char *absolute_path) {
+return mkdir(absolute_path, mode);
+});
 }
 
 TRACEPOINT(trace_vfs_rmdir, "\"%s\"", const char*);
@@ -1721,31 +1686,9 @@ int faccessat(int dirfd, const char *pathname, int mode, 
int flags)
 return access(pathname, mode);
 }
 
-struct file *fp;
-int error = fget(dirfd, &fp);
-if (error) {
-errno = error;
-return -1;
-}
-
-struct vnode *vp = fp->f_dentry->d_vnode;
-vn_lock(vp);
-
-std::unique_ptr up (new char[PATH_MAX]);
-char *p = up.g