On Wed, Apr 3, 2019 at 3:18 AM Dave Chinner <david@xxxxxxxxxxxxx> wrote: > > On Mon, Apr 01, 2019 at 01:50:18PM +0100, fdmanana@xxxxxxxxxx wrote: > > From: Filipe Manana <fdmanana@xxxxxxxx> > > > > Currently the fsync function can only be performed against regular files. > > Allow it to operate on directories too, to increase test coverage and > > allow for chances of finding bugs in a filesystem's implementation of > > fsync against directories. > > > > Signed-off-by: Filipe Manana <fdmanana@xxxxxxxx> > > --- > > > > V2: Added helper functions to open and close files or directories. > > not exactly what I meant, more below.... > > > > ltp/fsstress.c | 42 +++++++++++++++++++++++++++++++++++++++--- > > 1 file changed, 39 insertions(+), 3 deletions(-) > > > > diff --git a/ltp/fsstress.c b/ltp/fsstress.c > > index 2223fd7d..1169b840 100644 > > --- a/ltp/fsstress.c > > +++ b/ltp/fsstress.c > > @@ -303,6 +303,7 @@ int attr_remove_path(pathname_t *, const char *, int); > > int attr_set_path(pathname_t *, const char *, const char *, const int, int); > > void check_cwd(void); > > void cleanup_flist(void); > > +void close_file_or_dir(int, DIR *); > > int creat_path(pathname_t *, mode_t); > > void dcache_enter(int, int); > > void dcache_init(void); > > @@ -324,6 +325,7 @@ void make_freq_table(void); > > int mkdir_path(pathname_t *, mode_t); > > int mknod_path(pathname_t *, mode_t, dev_t); > > void namerandpad(int, char *, int); > > +int open_file_or_dir(pathname_t *, int, DIR **); > > int open_path(pathname_t *, int); > > DIR *opendir_path(pathname_t *); > > void process_freq(char *); > > @@ -852,6 +854,15 @@ cleanup_flist(void) > > } > > } > > > > +void > > +close_file_or_dir(int fd, DIR *dir) > > +{ > > + if (dir) > > + closedir(dir); > > + else > > + close(fd); > > +} > > + > > int > > creat_path(pathname_t *name, mode_t mode) > > { > > @@ -1385,6 +1396,30 @@ namerandpad(int id, char *buf, int i) > > } > > > > int > > +open_file_or_dir(pathname_t *name, int flags, DIR **dir) > > +{ > > + int fd; > > + > > + fd = open_path(name, flags); > > + if (fd < 0 && errno == EISDIR) { > > + *dir = opendir_path(name); > > Why this function, and not just open(O_DIRECTORY)? The reason I did it is because if flags are O_WRONLY (or O_RDWR), opening a directory always fails (even with O_DIRECTORY) with errno EISDIR. If the access flags are O_RDONLY, opening a directory succeeds regardless of O_DIRECTORY being passed to open(2). Since I supposed fsetxattr(2) and fsync(2) could be considered write operations, I went for the use of opendir_path(), using the dir stream and all that hassle. But now I just tested if fsync and fsetxattr work if the file is open O_RDONLY, and to my surprise they do. Test program: https://friendpaste.com/7gUN4kB3ZfHwVdlDggodUY Output with O_WRONLY | O_DIRECTORY passed to open(2): $ mkfs.xfs -f /dev/sdc $ mount /dev/sdc /mnt $ touch /mnt/file $ mkdir /mnt/dir $ ./test_open /mnt/file open error 20: Not a directory $ ./test_open /mnt/dir open error 21: Is a directory Output with only O_RDONLY passed to open(2): $ mkfs.xfs -f /dev/sdc $ mount /dev/sdc /mnt $ touch /mnt/file $ mkdir /mnt/dir $ ./test_open /mnt/file file open fd = 3, setxattr(fd) = 0, fsync(fd) = 0 $ ./test_open /mnt/dir file open fd = 3, setxattr(fd) = 0, fsync(fd) = 0 Perhaps I did something wrong, as it's late here right now. So should we go for ensuring O_RDONLY is used when the first open fails with EISDIR (and add some comment explaining this in case I'm not the only for which this was a surprise) Thanks. > None of the code that uses this function actually uses the DIR * > that is returned, just the fd that is extracted from it. So why no > just open() the directory directly and avoid having all the mess > with dir streams that aren't needed? > > > > + if (*dir) { > > + fd = dirfd(*dir); > > + if (fd < 0) { > > + int e = errno; > > + > > + closedir(*dir); > > + *dir = NULL; > > + errno = e; > > + } > > + } > > + } else { > > + *dir = NULL; > > + } > > + return fd; > > Excessive nesting. I think this should be as simple as: > > fd = open_path(name, flags); > if (fd < 0 && errno != EISDIR) { > return -1; > return open_path(name, flags | O_DIRECTORY); > > And the close_file_or_dir() function is completely unnecessary. > > > > + > > +int > > open_path(pathname_t *name, int oflag) > > { > > char buf[NAME_MAX + 1]; > > @@ -3440,15 +3475,16 @@ fsync_f(int opno, long r) > > pathname_t f; > > int fd; > > int v; > > + DIR *dir; > > > > init_pathname(&f); > > - if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) { > > + if (!get_fname(FT_REGFILE | FT_DIRm, r, &f, NULL, NULL, &v)) { > > if (v) > > printf("%d/%d: fsync - no filename\n", procid, opno); > > free_pathname(&f); > > return; > > } > > - fd = open_path(&f, O_WRONLY); > > + fd = open_file_or_dir(&f, O_WRONLY, &dir); > > e = fd < 0 ? errno : 0; > > check_cwd(); > > if (fd < 0) { > > This whole hunk - from init_pathname to check_cwd - was what I was > suggesting you factor out, not just the open_path() code. > > Cheers, > > Dave. > -- > Dave Chinner > david@xxxxxxxxxxxxx