From 27c51b5623123ba3208569ecdef343f61646c7e4 Mon Sep 17 00:00:00 2001 From: Patrick Farrell Date: Thu, 8 Mar 2018 05:47:56 -0600 Subject: [PATCH] LU-10337 mdt: Allow open of open orphans Standard open by handle behavior allows opening of open unlinked files files. This currently only works in Lustre if the file is already open on the same node, which is insufficient. When an open file is unlinked, we make it an orphan. These files can be recognized by checking their open count (mod_count). It's enough to just make opening these files possible, because the client cannot look them up to do an open except when using a file handle. Cray-bug-id: LUS-2626 Change-Id: Idd7898cefcf60b28c682e578774411e476216c9e Signed-off-by: Patrick Farrell Reviewed-on: https://review.whamcloud.com/30405 Reviewed-by: Andreas Dilger Tested-by: Jenkins Tested-by: Maloo Reviewed-by: Alexey Lyashkov Reviewed-by: James Simmons Reviewed-by: Andrew Perepechko Reviewed-by: Oleg Drokin --- lustre/mdd/mdd_internal.h | 5 + lustre/mdd/mdd_object.c | 7 +- lustre/tests/check_fhandle_syscalls.c | 220 ++++++++++++++++++---------------- lustre/tests/sanity.sh | 12 -- lustre/tests/sanityn.sh | 16 +++ 5 files changed, 141 insertions(+), 119 deletions(-) diff --git a/lustre/mdd/mdd_internal.h b/lustre/mdd/mdd_internal.h index 7d50e68..78863de 100644 --- a/lustre/mdd/mdd_internal.h +++ b/lustre/mdd/mdd_internal.h @@ -494,6 +494,11 @@ static inline bool mdd_is_volatile_obj(struct mdd_object *obj) return obj->mod_flags & VOLATILE_OBJ; } +static inline bool mdd_is_orphan_obj(struct mdd_object *obj) +{ + return obj->mod_flags & ORPHAN_OBJ; +} + static inline int mdd_object_exists(struct mdd_object *obj) { return lu_object_exists(mdd2lu_obj(obj)); diff --git a/lustre/mdd/mdd_object.c b/lustre/mdd/mdd_object.c index 3fedd2d..1025a0d 100644 --- a/lustre/mdd/mdd_object.c +++ b/lustre/mdd/mdd_object.c @@ -2988,8 +2988,11 @@ static int mdd_open_sanity_check(const struct lu_env *env, int mode, rc; ENTRY; - /* EEXIST check */ - if (mdd_is_dead_obj(obj)) + /* EEXIST check, also opening of *open* orphans is allowed so we can + * open-by-handle unlinked files + */ + if (mdd_is_dead_obj(obj) && + likely(!(mdd_is_orphan_obj(obj) && obj->mod_count > 0))) RETURN(-ENOENT); if (S_ISLNK(attr->la_mode)) diff --git a/lustre/tests/check_fhandle_syscalls.c b/lustre/tests/check_fhandle_syscalls.c index 7080ee8..4806974 100644 --- a/lustre/tests/check_fhandle_syscalls.c +++ b/lustre/tests/check_fhandle_syscalls.c @@ -47,7 +47,30 @@ #define MAX_HANDLE_SZ 128 -#if !defined(HAVE_FHANDLE_GLIBC_SUPPORT) && defined(HAVE_FHANDLE_SYSCALLS) +void usage(char *prog) +{ + fprintf(stderr, "usage: %s \n", + prog); + fprintf(stderr, "the absolute path of a test file on a " + "lustre file system is needed.\n"); + exit(1); +} + + +#ifndef HAVE_FHANDLE_SYSCALLS + +int main(int argc, char **argv) +{ + if (argc != 3) + usage(argv[0]); + + fprintf(stderr, "HAVE_FHANDLE_SYSCALLS not defined\n"); + return 0; +} + +#else + +#ifndef HAVE_FHANDLE_GLIBC_SUPPORT /* Because the kernel supports this functions doesn't mean that glibc does. * Just in case we define what we need */ struct file_handle { @@ -87,7 +110,6 @@ struct file_handle { #define __NR_open_by_handle_at 265 #endif - #endif static inline int @@ -105,29 +127,82 @@ open_by_handle_at(int mnt_fd, struct file_handle *fh, int mode) } #endif -void usage(char *prog) +/* verify a file contents */ +int check_access(const char *filename, + int mnt_fd, struct file_handle *fh, struct stat *st_orig) { - fprintf(stderr, "usage: %s \n", - prog); - fprintf(stderr, "the absolute path of a test file on a " - "lustre file system is needed.\n"); - exit(1); + int fd2, rc, len, offset; + struct stat st; + char *readbuf = NULL; + + /* Open the file handle */ + fd2 = open_by_handle_at(mnt_fd, fh, O_RDONLY); + if (fd2 < 0) { + fprintf(stderr, "open_by_handle_at(%s) error: %s\n", filename, + strerror(errno)); + rc = errno; + goto out_f_handle; + } + + /* Get file size */ + bzero(&st, sizeof(struct stat)); + rc = fstat(fd2, &st); + if (rc < 0) { + fprintf(stderr, "fstat(%s) error: %s\n", filename, + strerror(errno)); + rc = errno; + goto out_fd2; + } + + /* we can't check a ctime due unlink update */ + if (st_orig->st_size != st.st_size || + st_orig->st_ino != st.st_ino || + st_orig->st_mtime != st.st_mtime) { + fprintf(stderr, "stat data does not match between fopen " + "and fhandle case\n"); + rc = EINVAL; + goto out_fd2; + } + + if (st.st_size) { + len = st.st_blksize; + readbuf = malloc(len); + if (readbuf == NULL) { + fprintf(stderr, "malloc(%d) error: %s\n", len, + strerror(errno)); + rc = errno; + goto out_fd2; + } + + for (offset = 0; offset < st.st_size; offset += len) { + /* read from the file */ + rc = read(fd2, readbuf, len); + if (rc < 0) { + fprintf(stderr, "read(%s) error: %s\n", + filename, strerror(errno)); + rc = errno; + goto out_readbuf; + } + } + } + rc = 0; +out_readbuf: + free(readbuf); +out_fd2: + close(fd2); +out_f_handle: + return rc; } int main(int argc, char **argv) { -#ifdef HAVE_FHANDLE_SYSCALLS - char *filename, *file, *mount_point = NULL, *readbuf = NULL; - int ret, rc = -EINVAL, mnt_id, mnt_fd, fd1, fd2, i, len, offset; + char *filename, *file; + int ret, rc = -EINVAL, mnt_fd, mnt_id, fd1, i; struct file_handle *fh = NULL; - int file_size, mtime, ctime; struct lu_fid *parent, *fid; - struct mntent *ent; struct stat st; - __ino_t inode; - FILE *mntpt; - if (argc != 2) + if (argc != 3) usage(argv[0]); file = argv[1]; @@ -136,6 +211,12 @@ int main(int argc, char **argv) goto out; } + if (*argv[2] != '/') { + fprintf(stderr, "Need the absolete path of the mount point\n"); + goto out; + } + filename = rindex(file, '/') + 1; + fd1 = open(file, O_RDONLY); if (fd1 < 0) { fprintf(stderr, "open file %s error: %s\n", @@ -154,41 +235,10 @@ int main(int argc, char **argv) goto out_fd1; } - inode = st.st_ino; - mtime = st.st_mtime; - ctime = st.st_ctime; - file_size = st.st_size; - - /* Now for the setup to use fhandles */ - mntpt = setmntent("/etc/mtab", "r"); - if (mntpt == NULL) { - fprintf(stderr, "setmntent error: %s\n", - strerror(errno)); - rc = errno; - goto out_fd1; - } - - while (NULL != (ent = getmntent(mntpt))) { - if ((strncmp(file, ent->mnt_dir, strlen(ent->mnt_dir)) == 0) && - (strcmp(ent->mnt_type, "lustre") == 0)) { - mount_point = ent->mnt_dir; - break; - } - } - endmntent(mntpt); - - if (mount_point == NULL) { - fprintf(stderr, "file is not located on a lustre file " - "system?\n"); - goto out_fd1; - } - - filename = rindex(file, '/') + 1; - /* Open mount point directory */ - mnt_fd = open(mount_point, O_DIRECTORY); + mnt_fd = open(argv[2], O_DIRECTORY); if (mnt_fd < 0) { - fprintf(stderr, "open(%s) error: %s\n)", mount_point, + fprintf(stderr, "open(%s) error: %s\n)", argv[2], strerror(errno)); rc = errno; goto out_fd1; @@ -205,7 +255,7 @@ int main(int argc, char **argv) fh->handle_bytes = MAX_HANDLE_SZ; /* Convert name to handle */ - ret = name_to_handle_at(mnt_fd, filename, fh, &mnt_id, + ret = name_to_handle_at(AT_FDCWD, file, fh, &mnt_id, AT_SYMLINK_FOLLOW); if (ret) { fprintf(stderr, "name_by_handle_at(%s) error: %s\n", filename, @@ -228,62 +278,27 @@ int main(int argc, char **argv) fprintf(stdout, "file's parent FID is "DFID"\n", PFID(parent)); fprintf(stdout, "file FID is "DFID"\n", PFID(fid)); - /* Open the file handle */ - fd2 = open_by_handle_at(mnt_fd, fh, O_RDONLY); - if (fd2 < 0) { - fprintf(stderr, "open_by_handle_at(%s) error: %s\n", filename, - strerror(errno)); - rc = errno; + fprintf(stdout, "just access via different mount point - "); + rc = check_access(filename, mnt_fd, fh, &st); + if (rc != 0) goto out_f_handle; - } - - /* Get file size */ - bzero(&st, sizeof(struct stat)); - rc = fstat(fd2, &st); - if (rc < 0) { - fprintf(stderr, "fstat(%s) error: %s\n", filename, - strerror(errno)); - rc = errno; - goto out_fd2; - } + fprintf(stdout, "OK \n"); - if (ctime != st.st_ctime || file_size != st.st_size || - inode != st.st_ino || mtime != st.st_mtime) { - fprintf(stderr, "stat data does not match between fopen " - "and fhandle case\n"); - goto out_fd2; + fprintf(stdout, "access after unlink - "); + ret = unlink(file); + if (ret < 0) { + fprintf(stderr, "can't unlink a file. check permissions?\n"); + goto out_f_handle; } - if (st.st_size) { - len = st.st_blksize; - readbuf = malloc(len); - if (readbuf == NULL) { - fprintf(stderr, "malloc(%d) error: %s\n", len, - strerror(errno)); - rc = errno; - goto out_fd2; - } - - for (offset = 0; offset < st.st_size; offset += len) { - /* read from the file */ - rc = read(fd2, readbuf, len); - if (rc < 0) { - fprintf(stderr, "read(%s) error: %s\n", - filename, strerror(errno)); - rc = errno; - goto out_readbuf; - } - } - } + rc = check_access(filename, mnt_fd, fh, &st); + if (rc != 0) + goto out_f_handle; + fprintf(stdout, "OK\n"); rc = 0; fprintf(stdout, "check_fhandle_syscalls test Passed!\n"); -out_readbuf: - if (readbuf != NULL) - free(readbuf); -out_fd2: - close(fd2); out_f_handle: free(fh); out_mnt_fd: @@ -292,11 +307,6 @@ out_fd1: close(fd1); out: return rc; -#else /* !HAVE_FHANDLE_SYSCALLS */ - if (argc != 2) - usage(argv[0]); - - fprintf(stderr, "HAVE_FHANDLE_SYSCALLS not defined\n"); - return 0; -#endif /* HAVE_FHANDLE_SYSCALLS */ } + +#endif diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index 45920b4..4e192ec 100755 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -15602,18 +15602,6 @@ test_236() { } run_test 236 "Layout swap on open unlinked file" -# test to verify file handle related system calls -# (name_to_handle_at/open_by_handle_at) -# The new system calls are supported in glibc >= 2.14. - -test_237() { - echo "Test file_handle syscalls" > $DIR/$tfile || - error "write failed" - check_fhandle_syscalls $DIR/$tfile || - error "check_fhandle_syscalls failed" -} -run_test 237 "Verify name_to_handle_at/open_by_handle_at syscalls" - # LU-4659 linkea consistency test_238() { local server_version=$(lustre_version_code $SINGLEMDS) diff --git a/lustre/tests/sanityn.sh b/lustre/tests/sanityn.sh index 4cd17e5..d77c7ba 100755 --- a/lustre/tests/sanityn.sh +++ b/lustre/tests/sanityn.sh @@ -4469,6 +4469,22 @@ test_101c() { } run_test 101c "Discard DoM data on close-unlink" +# test to verify file handle related system calls +# (name_to_handle_at/open_by_handle_at) +# The new system calls are supported in glibc >= 2.14. + +# test to verify we can open by handle an unlinked file from > 1 client +# This test opens the file normally on $DIR1, which is on one mount, and then +# opens it by handle on $DIR2, which is on a different mount. +test_102() { + echo "Test file_handle syscalls" > $DIR/$tfile || + error "write failed" + check_fhandle_syscalls $DIR/$tfile $DIR2 || + error "check_fhandle_syscalls failed" + rm -f $DIR2/$tfile +} +run_test 102 "Test open by handle of unlinked file" + log "cleanup: ======================================================" # kill and wait in each test only guarentee script finish, but command in script -- 1.8.3.1