Re: [osv-dev] [PATCH] unlinkat: fill the gaps in the implementation
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
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
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
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
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