From: Venky Shankar Date: Mon, 12 Apr 2021 09:25:34 +0000 (-0400) Subject: libcephfs: introduce basic *at() calls X-Git-Tag: v17.1.0~2137^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=3831aa12f3067d8cc362f39f7136dd53cb946d22;p=ceph.git libcephfs: introduce basic *at() calls Signed-off-by: Venky Shankar --- diff --git a/src/client/Client.cc b/src/client/Client.cc index a99e1cd6ad8..f598600fc5c 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -204,6 +204,21 @@ int Client::CommandHook::call( // ------------- +int Client::get_fd_inode(int fd, InodeRef *in) { + int r = 0; + if (fd == CEPHFS_AT_FDCWD) { + *in = cwd; + } else { + Fh *f = get_filehandle(fd); + if (!f) { + r = -CEPHFS_EBADF; + } else { + *in = f->inode; + } + } + return r; +} + dir_result_t::dir_result_t(Inode *in, const UserPerm& perms) : inode(in), offset(0), next_offset(2), release_count(0), ordered_count(0), cache_index(0), start_shared_gen(0), @@ -6916,25 +6931,30 @@ int Client::walk(std::string_view path, walk_dentry_result* wdr, const UserPerm& } int Client::path_walk(const filepath& origpath, InodeRef *end, - const UserPerm& perms, bool followsym, int mask) + const UserPerm& perms, bool followsym, int mask, InodeRef dirinode) { walk_dentry_result wdr; - int rc = path_walk(origpath, &wdr, perms, followsym, mask); + int rc = path_walk(origpath, &wdr, perms, followsym, mask, dirinode); *end = std::move(wdr.in); return rc; } -int Client::path_walk(const filepath& origpath, walk_dentry_result* result, const UserPerm& perms, bool followsym, int mask) +int Client::path_walk(const filepath& origpath, walk_dentry_result* result, const UserPerm& perms, + bool followsym, int mask, InodeRef dirinode) { filepath path = origpath; InodeRef cur; std::string alternate_name; if (origpath.absolute()) cur = root; - else + else if (!dirinode) cur = cwd; + else { + cur = dirinode; + } ceph_assert(cur); + ldout(cct, 20) << __func__ << " cur=" << *cur << dendl; ldout(cct, 10) << __func__ << " " << path << dendl; int symlinks = 0; @@ -7085,6 +7105,53 @@ int Client::unlink(const char *relpath, const UserPerm& perm) return _unlink(dir.get(), name.c_str(), perm); } +int Client::unlinkat(int dirfd, const char *relpath, int flags, const UserPerm& perm) +{ + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + tout(cct) << __func__ << std::endl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + tout(cct) << flags << std::endl; + + if (std::string(relpath) == "/") { + return flags & AT_REMOVEDIR ? -CEPHFS_EBUSY : -CEPHFS_EISDIR; + } + + filepath path(relpath); + string name = path.last_dentry(); + path.pop_dentry(); + InodeRef dir; + + std::scoped_lock lock(client_lock); + + InodeRef dirinode; + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + + r = path_walk(path, &dir, perm, true, 0, dirinode); + if (r < 0) { + return r; + } + if (cct->_conf->client_permissions) { + r = may_delete(dir.get(), name.c_str(), perm); + if (r < 0) { + return r; + } + } + if (flags & AT_REMOVEDIR) { + r = _rmdir(dir.get(), name.c_str(), perm); + } else { + r = _unlink(dir.get(), name.c_str(), perm); + } + return r; +} + int Client::rename(const char *relfrom, const char *relto, const UserPerm& perm, std::string alternate_name) { RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); @@ -7161,6 +7228,49 @@ int Client::mkdir(const char *relpath, mode_t mode, const UserPerm& perm, std::s return _mkdir(dir.get(), name.c_str(), mode, perm, 0, {}, std::move(alternate_name)); } +int Client::mkdirat(int dirfd, const char *relpath, mode_t mode, const UserPerm& perm, + std::string alternate_name) +{ + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) + return -CEPHFS_ENOTCONN; + + tout(cct) << __func__ << std::endl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + tout(cct) << mode << std::endl; + ldout(cct, 10) << __func__ << ": " << relpath << dendl; + + if (std::string(relpath) == "/") { + return -CEPHFS_EEXIST; + } + + filepath path(relpath); + string name = path.last_dentry(); + path.pop_dentry(); + InodeRef dir; + + std::scoped_lock lock(client_lock); + + InodeRef dirinode; + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + + r = path_walk(path, &dir, perm, true, 0, dirinode); + if (r < 0) { + return r; + } + if (cct->_conf->client_permissions) { + r = may_create(dir.get(), perm); + if (r < 0) { + return r; + } + } + return _mkdir(dir.get(), name.c_str(), mode, perm, 0, {}, std::move(alternate_name)); +} + int Client::mkdirs(const char *relpath, mode_t mode, const UserPerm& perms) { RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); @@ -7310,6 +7420,48 @@ int Client::symlink(const char *target, const char *relpath, const UserPerm& per return _symlink(dir.get(), name.c_str(), target, perms, std::move(alternate_name)); } +int Client::symlinkat(const char *target, int dirfd, const char *relpath, const UserPerm& perms, + std::string alternate_name) +{ + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + tout(cct) << __func__ << std::endl; + tout(cct) << target << std::endl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + + if (std::string(relpath) == "/") { + return -CEPHFS_EEXIST; + } + + filepath path(relpath); + string name = path.last_dentry(); + path.pop_dentry(); + InodeRef dir; + + std::scoped_lock lock(client_lock); + + InodeRef dirinode; + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + r = path_walk(path, &dir, perms, true, 0, dirinode); + if (r < 0) { + return r; + } + if (cct->_conf->client_permissions) { + int r = may_create(dir.get(), perms); + if (r < 0) { + return r; + } + } + return _symlink(dir.get(), name.c_str(), target, perms, std::move(alternate_name)); +} + int Client::readlink(const char *relpath, char *buf, loff_t size, const UserPerm& perms) { RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); @@ -7330,6 +7482,33 @@ int Client::readlink(const char *relpath, char *buf, loff_t size, const UserPerm return _readlink(in.get(), buf, size); } +int Client::readlinkat(int dirfd, const char *relpath, char *buf, loff_t size, const UserPerm& perms) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + tout(cct) << __func__ << std::endl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + + InodeRef dirinode; + std::scoped_lock lock(client_lock); + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + + InodeRef in; + filepath path(relpath); + r = path_walk(path, &in, perms, false, 0, dirinode); + if (r < 0) { + return r; + } + + return _readlink(in.get(), buf, size); +} + int Client::_readlink(Inode *in, char *buf, size_t size) { if (!in->is_symlink()) @@ -8019,6 +8198,38 @@ int Client::fchmod(int fd, mode_t mode, const UserPerm& perms) return _setattr(f->inode, &attr, CEPH_SETATTR_MODE, perms); } +int Client::chmodat(int dirfd, const char *relpath, mode_t mode, int flags, + const UserPerm& perms) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + tout(cct) << __func__ << std::endl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + tout(cct) << mode << std::endl; + tout(cct) << flags << std::endl; + + filepath path(relpath); + InodeRef in; + InodeRef dirinode; + + std::scoped_lock lock(client_lock); + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + + r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), 0, dirinode); + if (r < 0) { + return r; + } + struct stat attr; + attr.st_mode = mode; + return _setattr(in, &attr, CEPH_SETATTR_MODE, perms); +} + int Client::lchmod(const char *relpath, mode_t mode, const UserPerm& perms) { RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); @@ -8124,6 +8335,40 @@ int Client::lchown(const char *relpath, uid_t new_uid, gid_t new_gid, return _setattr(in, &attr, mask, perms); } +int Client::chownat(int dirfd, const char *relpath, uid_t new_uid, gid_t new_gid, + int flags, const UserPerm& perms) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + tout(cct) << __func__ << std::endl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + tout(cct) << new_uid << std::endl; + tout(cct) << new_gid << std::endl; + tout(cct) << flags << std::endl; + + filepath path(relpath); + InodeRef in; + InodeRef dirinode; + + std::scoped_lock lock(client_lock); + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + + r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), 0, dirinode); + if (r < 0) { + return r; + } + struct stat attr; + attr.st_uid = new_uid; + attr.st_gid = new_gid; + return _setattr(in, &attr, CEPH_SETATTR_UID|CEPH_SETATTR_GID, perms); +} + static void attr_set_atime_and_mtime(struct stat *attr, const utime_t &atime, const utime_t &mtime) @@ -8270,6 +8515,50 @@ int Client::futimens(int fd, struct timespec times[2], const UserPerm& perms) return _setattr(f->inode, &attr, CEPH_SETATTR_MTIME|CEPH_SETATTR_ATIME, perms); } +int Client::utimensat(int dirfd, const char *relpath, struct timespec times[2], int flags, + const UserPerm& perms) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + tout(cct) << __func__ << std::endl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + tout(cct) << "atime: " << times[0].tv_sec << "." << times[0].tv_nsec + << std::endl; + tout(cct) << "mtime: " << times[1].tv_sec << "." << times[1].tv_nsec + << std::endl; + tout(cct) << flags << std::endl; + + filepath path(relpath); + InodeRef in; + InodeRef dirinode; + + std::scoped_lock lock(client_lock); + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + +#if defined(__linux__) && defined(O_PATH) + if (flags & O_PATH) { + return -CEPHFS_EBADF; + } +#endif + + r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), 0, dirinode); + if (r < 0) { + return r; + } + struct stat attr; + utime_t atime(times[0]); + utime_t mtime(times[1]); + + attr_set_atime_and_mtime(&attr, atime, mtime); + return _setattr(in, &attr, CEPH_SETATTR_MTIME|CEPH_SETATTR_ATIME, perms); +} + int Client::flock(int fd, int operation, uint64_t owner) { RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); @@ -8317,6 +8606,36 @@ int Client::opendir(const char *relpath, dir_result_t **dirpp, const UserPerm& p return r; } +int Client::fdopendir(int dirfd, dir_result_t **dirpp, const UserPerm &perms) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + tout(cct) << __func__ << std::endl; + tout(cct) << dirfd << std::endl; + + InodeRef dirinode; + std::scoped_lock locker(client_lock); + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + + if (cct->_conf->client_permissions) { + r = may_open(dirinode.get(), O_RDONLY, perms); + if (r < 0) { + return r; + } + } + r = _opendir(dirinode.get(), dirpp, perms); + /* if ENOTDIR, dirpp will be an uninitialized point and it's very dangerous to access its value */ + if (r != -CEPHFS_ENOTDIR) { + tout(cct) << (uintptr_t)*dirpp << std::endl; + } + return r; +} + int Client::_opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms) { if (!in->is_dir()) @@ -9015,19 +9334,14 @@ int Client::getdir(const char *relpath, list& contents, /****** file i/o **********/ -int Client::open(const char *relpath, int flags, const UserPerm& perms, - mode_t mode, int stripe_unit, int stripe_count, - int object_size, const char *data_pool, std::string alternate_name) -{ - RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); - if (!mref_reader.is_state_satisfied()) - return -CEPHFS_ENOTCONN; +// common parts for open and openat. call with client_lock locked. +int Client::create_and_open(std::optional dirfd, const char *relpath, int flags, + const UserPerm& perms, mode_t mode, int stripe_unit, + int stripe_count, int object_size, const char *data_pool, + std::string alternate_name) { + ceph_assert(ceph_mutex_is_locked(client_lock)); int cflags = ceph_flags_sys2wire(flags); - - ldout(cct, 3) << "open enter(" << relpath << ", " << cflags << "," << mode << ")" << dendl; - tout(cct) << "open" << std::endl; - tout(cct) << relpath << std::endl; tout(cct) << cflags << std::endl; Fh *fh = NULL; @@ -9047,16 +9361,22 @@ int Client::open(const char *relpath, int flags, const UserPerm& perms, bool followsym = !((flags & O_NOFOLLOW) || ((flags & O_CREAT) && (flags & O_EXCL))); int mask = ceph_caps_for_mode(ceph_flags_to_mode(cflags)); - std::scoped_lock lock(client_lock); - int r = path_walk(path, &in, perms, followsym, mask); + InodeRef dirinode = nullptr; + if (dirfd) { + int r = get_fd_inode(*dirfd, &dirinode); + if (r < 0) { + return r; + } + } + int r = path_walk(path, &in, perms, followsym, mask, dirinode); if (r == 0 && (flags & O_CREAT) && (flags & O_EXCL)) return -CEPHFS_EEXIST; #if defined(__linux__) && defined(O_PATH) if (r == 0 && in->is_symlink() && (flags & O_NOFOLLOW) && !(flags & O_PATH)) #else - if (r == 0 && in->is_symlink() && (flags & O_NOFOLLOW)) + if (r == 0 && in->is_symlink() && (flags & O_NOFOLLOW)) #endif return -CEPHFS_ELOOP; @@ -9066,13 +9386,14 @@ int Client::open(const char *relpath, int flags, const UserPerm& perms, dirpath.pop_dentry(); InodeRef dir; r = path_walk(dirpath, &dir, perms, true, - cct->_conf->client_permissions ? CEPH_CAP_AUTH_SHARED : 0); - if (r < 0) + cct->_conf->client_permissions ? CEPH_CAP_AUTH_SHARED : 0, dirinode); + if (r < 0) { goto out; + } if (cct->_conf->client_permissions) { r = may_create(dir.get(), perms); if (r < 0) - goto out; + goto out; } r = _create(dir.get(), dname.c_str(), flags, mode, &in, &fh, stripe_unit, stripe_count, object_size, data_pool, &created, perms, @@ -9086,7 +9407,7 @@ int Client::open(const char *relpath, int flags, const UserPerm& perms, if (cct->_conf->client_permissions) { r = may_open(in.get(), flags, perms); if (r < 0) - goto out; + goto out; } } @@ -9101,8 +9422,42 @@ int Client::open(const char *relpath, int flags, const UserPerm& perms, } out: + return r; +} + +int Client::open(const char *relpath, int flags, const UserPerm& perms, + mode_t mode, int stripe_unit, int stripe_count, + int object_size, const char *data_pool, std::string alternate_name) +{ + return openat(CEPHFS_AT_FDCWD, relpath, flags, perms, mode, stripe_unit, + stripe_count, object_size, data_pool, alternate_name); +} + +int Client::_openat(int dirfd, const char *relpath, int flags, const UserPerm& perms, + mode_t mode, std::string alternate_name) { + return create_and_open(dirfd, relpath, flags, perms, mode, 0, 0, 0, NULL, alternate_name); +} + +int Client::openat(int dirfd, const char *relpath, int flags, const UserPerm& perms, + mode_t mode, int stripe_unit, int stripe_count, int object_size, + const char *data_pool, std::string alternate_name) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + ldout(cct, 3) << "openat enter(" << relpath << ")" << dendl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + tout(cct) << flags << std::endl; + tout(cct) << mode << std::endl; + + std::scoped_lock locker(client_lock); + int r = create_and_open(dirfd, relpath, flags, perms, mode, stripe_unit, stripe_count, + object_size, data_pool, alternate_name); + tout(cct) << r << std::endl; - ldout(cct, 3) << "open exit(" << path << ", " << cflags << ") = " << r << dendl; + ldout(cct, 3) << "openat exit(" << relpath << ")" << dendl; return r; } @@ -9434,17 +9789,12 @@ int Client::_renew_caps(Inode *in) return ret; } -int Client::close(int fd) +int Client::_close(int fd) { - RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); - if (!mref_reader.is_state_satisfied()) - return -CEPHFS_ENOTCONN; - ldout(cct, 3) << "close enter(" << fd << ")" << dendl; tout(cct) << "close" << std::endl; tout(cct) << fd << std::endl; - std::scoped_lock lock(client_lock); Fh *fh = get_filehandle(fd); if (!fh) return -CEPHFS_EBADF; @@ -9455,6 +9805,14 @@ int Client::close(int fd) return err; } +int Client::close(int fd) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) + return -CEPHFS_ENOTCONN; + + std::scoped_lock lock(client_lock); + return _close(fd); +} // ------------ // read, write @@ -10518,6 +10876,44 @@ int Client::fstatx(int fd, struct ceph_statx *stx, const UserPerm& perms, return r; } +int Client::statxat(int dirfd, const char *relpath, + struct ceph_statx *stx, const UserPerm& perms, + unsigned int want, unsigned int flags) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -CEPHFS_ENOTCONN; + } + + tout(cct) << __func__ << " flags " << hex << flags << " want " << want << dec << std::endl; + tout(cct) << dirfd << std::endl; + tout(cct) << relpath << std::endl; + + unsigned mask = statx_to_mask(flags, want); + + InodeRef dirinode; + std::scoped_lock lock(client_lock); + int r = get_fd_inode(dirfd, &dirinode); + if (r < 0) { + return r; + } + + InodeRef in; + filepath path(relpath); + r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), mask, dirinode); + if (r < 0) { + return r; + } + r = _getattr(in, mask, perms); + if (r < 0) { + ldout(cct, 3) << __func__ << " exit on error!" << dendl; + return r; + } + + fill_statx(in, mask, stx); + ldout(cct, 3) << __func__ << " dirfd" << dirfd << ", r= " << r << dendl; + return r; +} + // not written yet, but i want to link! int Client::chdir(const char *relpath, std::string &new_cwd, diff --git a/src/client/Client.h b/src/client/Client.h index 89a0694124e..13fc2609ca8 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -331,6 +331,7 @@ public: // namespace ops int opendir(const char *name, dir_result_t **dirpp, const UserPerm& perms); + int fdopendir(int dirfd, dir_result_t **dirpp, const UserPerm& perms); int closedir(dir_result_t *dirp); /** @@ -372,17 +373,23 @@ public: int may_delete(const char *relpath, const UserPerm& perms); int link(const char *existing, const char *newname, const UserPerm& perm, std::string alternate_name=""); int unlink(const char *path, const UserPerm& perm); + int unlinkat(int dirfd, const char *relpath, int flags, const UserPerm& perm); int rename(const char *from, const char *to, const UserPerm& perm, std::string alternate_name=""); // dirs int mkdir(const char *path, mode_t mode, const UserPerm& perm, std::string alternate_name=""); + int mkdirat(int dirfd, const char *relpath, mode_t mode, const UserPerm& perm, + std::string alternate_name=""); int mkdirs(const char *path, mode_t mode, const UserPerm& perms); int rmdir(const char *path, const UserPerm& perms); // symlinks int readlink(const char *path, char *buf, loff_t size, const UserPerm& perms); + int readlinkat(int dirfd, const char *relpath, char *buf, loff_t size, const UserPerm& perms); int symlink(const char *existing, const char *newname, const UserPerm& perms, std::string alternate_name=""); + int symlinkat(const char *target, int dirfd, const char *relpath, const UserPerm& perms, + std::string alternate_name=""); // path traversal for high-level interface int walk(std::string_view path, struct walk_dentry_result* result, const UserPerm& perms, bool followsym=true); @@ -405,12 +412,15 @@ public: int fsetattrx(int fd, struct ceph_statx *stx, int mask, const UserPerm& perms); int chmod(const char *path, mode_t mode, const UserPerm& perms); int fchmod(int fd, mode_t mode, const UserPerm& perms); + int chmodat(int dirfd, const char *relpath, mode_t mode, int flags, const UserPerm& perms); int lchmod(const char *path, mode_t mode, const UserPerm& perms); int chown(const char *path, uid_t new_uid, gid_t new_gid, const UserPerm& perms); int fchown(int fd, uid_t new_uid, gid_t new_gid, const UserPerm& perms); int lchown(const char *path, uid_t new_uid, gid_t new_gid, const UserPerm& perms); + int chownat(int dirfd, const char *relpath, uid_t new_uid, gid_t new_gid, + int flags, const UserPerm& perms); int utime(const char *path, struct utimbuf *buf, const UserPerm& perms); int lutime(const char *path, struct utimbuf *buf, const UserPerm& perms); int futime(int fd, struct utimbuf *buf, const UserPerm& perms); @@ -418,21 +428,38 @@ public: int lutimes(const char *relpath, struct timeval times[2], const UserPerm& perms); int futimes(int fd, struct timeval times[2], const UserPerm& perms); int futimens(int fd, struct timespec times[2], const UserPerm& perms); + int utimensat(int dirfd, const char *relpath, struct timespec times[2], int flags, + const UserPerm& perms); int flock(int fd, int operation, uint64_t owner); int truncate(const char *path, loff_t size, const UserPerm& perms); // file ops int mknod(const char *path, mode_t mode, const UserPerm& perms, dev_t rdev=0); + + int create_and_open(std::optional dirfd, const char *relpath, int flags, const UserPerm& perms, + mode_t mode, int stripe_unit, int stripe_count, int object_size, const char *data_pool, + std::string alternate_name); int open(const char *path, int flags, const UserPerm& perms, mode_t mode=0, std::string alternate_name="") { return open(path, flags, perms, mode, 0, 0, 0, NULL, alternate_name); } int open(const char *path, int flags, const UserPerm& perms, mode_t mode, int stripe_unit, int stripe_count, int object_size, const char *data_pool, std::string alternate_name=""); + int _openat(int dirfd, const char *relpath, int flags, const UserPerm& perms, + mode_t mode=0, std::string alternate_name=""); + int openat(int dirfd, const char *relpath, int flags, const UserPerm& perms, + mode_t mode, int stripe_unit, int stripe_count, + int object_size, const char *data_pool, std::string alternate_name); + int openat(int dirfd, const char *path, int flags, const UserPerm& perms, mode_t mode=0, + std::string alternate_name="") { + return openat(dirfd, path, flags, perms, mode, 0, 0, 0, NULL, alternate_name); + } + int lookup_hash(inodeno_t ino, inodeno_t dirino, const char *name, const UserPerm& perms); int lookup_ino(inodeno_t ino, const UserPerm& perms, Inode **inode=NULL); int lookup_name(Inode *in, Inode *parent, const UserPerm& perms); + int _close(int fd); int close(int fd); loff_t lseek(int fd, loff_t offset, int whence); int read(int fd, char *buf, loff_t size, loff_t offset=-1); @@ -446,6 +473,9 @@ public: int mask=CEPH_STAT_CAP_INODE_ALL); int fstatx(int fd, struct ceph_statx *stx, const UserPerm& perms, unsigned int want, unsigned int flags); + int statxat(int dirfd, const char *relpath, + struct ceph_statx *stx, const UserPerm& perms, + unsigned int want, unsigned int flags); int fallocate(int fd, int mode, loff_t offset, loff_t length); // full path xattr ops @@ -909,9 +939,10 @@ protected: void handle_client_reply(const MConstRef& reply); bool is_dir_operation(MetaRequest *request); - int path_walk(const filepath& fp, struct walk_dentry_result* result, const UserPerm& perms, bool followsym=true, int mask=0); + int path_walk(const filepath& fp, struct walk_dentry_result* result, const UserPerm& perms, bool followsym=true, int mask=0, + InodeRef dirinode=nullptr); int path_walk(const filepath& fp, InodeRef *end, const UserPerm& perms, - bool followsym=true, int mask=0); + bool followsym=true, int mask=0, InodeRef dirinode=nullptr); // fake inode number for 32-bits ino_t void _assign_faked_ino(Inode *in); @@ -950,6 +981,7 @@ protected: return NULL; return it->second; } + int get_fd_inode(int fd, InodeRef *in); // helpers void wake_up_session_caps(MetaSession *s, bool reconnect); diff --git a/src/include/cephfs/libcephfs.h b/src/include/cephfs/libcephfs.h index d8042f5c5fe..f3711435104 100644 --- a/src/include/cephfs/libcephfs.h +++ b/src/include/cephfs/libcephfs.h @@ -533,6 +533,16 @@ int ceph_chdir(struct ceph_mount_info *cmount, const char *path); */ int ceph_opendir(struct ceph_mount_info *cmount, const char *name, struct ceph_dir_result **dirpp); +/** + * Open a directory referred to by a file descriptor + * + * @param cmount the ceph mount handle to use to open the directory + * @param dirfd open file descriptor for the directory + * @param dirpp the directory result pointer structure to fill in + * @returns 0 on success or negative error code otherwise + */ +int ceph_fdopendir(struct ceph_mount_info *cmount, int dirfd, struct ceph_dir_result **dirpp); + /** * Close the open directory. * @@ -651,6 +661,17 @@ void ceph_seekdir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, */ int ceph_mkdir(struct ceph_mount_info *cmount, const char *path, mode_t mode); +/** + * Create a directory relative to a file descriptor + * + * @param cmount the ceph mount handle to use for making the directory. + * @param dirfd open file descriptor for a directory (or CEPHFS_AT_FDCWD) + * @param relpath the path of the directory to create. + * @param mode the permissions the directory should have once created. + * @returns 0 on success or a negative return code on error. + */ +int ceph_mkdirat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, mode_t mode); + /** * Create a snapshot * @@ -727,6 +748,19 @@ int ceph_link(struct ceph_mount_info *cmount, const char *existing, const char * */ int ceph_readlink(struct ceph_mount_info *cmount, const char *path, char *buf, int64_t size); +/** + * Read a symbolic link relative to a file descriptor + * + * @param cmount the ceph mount handle to use for creating the link. + * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD) + * @param relpath the path to the symlink to read + * @param buf the buffer to hold the path of the file that the symlink points to. + * @param size the length of the buffer + * @returns number of bytes copied on success or negative error code on failure + */ +int ceph_readlinkat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, char *buf, + int64_t size); + /** * Creates a symbolic link. * @@ -737,6 +771,18 @@ int ceph_readlink(struct ceph_mount_info *cmount, const char *path, char *buf, i */ int ceph_symlink(struct ceph_mount_info *cmount, const char *existing, const char *newname); +/** + * Creates a symbolic link relative to a file descriptor + * + * @param cmount the ceph mount handle to use for creating the symbolic link. + * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD) + * @param existing the path to the existing file/directory to link to. + * @param newname the path to the new file/directory to link from. + * @returns 0 on success or a negative return code on failure. + */ +int ceph_symlinkat(struct ceph_mount_info *cmount, const char *existing, int dirfd, + const char *newname); + /** @} links */ /** @@ -766,6 +812,19 @@ int ceph_may_delete(struct ceph_mount_info *cmount, const char *path); */ int ceph_unlink(struct ceph_mount_info *cmount, const char *path); +/** + * Removes a file, link, or symbolic link relative to a file descriptor. + * If the file/link has multiple links to it, the file will not + * disappear from the namespace until all references to it are removed. + * + * @param cmount the ceph mount handle to use for performing the unlink. + * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD) + * @param relpath the path of the file or link to unlink. + * @param flags bitfield that can be used to set AT_* modifier flags (only AT_REMOVEDIR) + * @returns 0 on success or negative error code on failure. + */ +int ceph_unlinkat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, int flags); + /** * Rename a file or directory. * @@ -789,6 +848,20 @@ int ceph_rename(struct ceph_mount_info *cmount, const char *from, const char *to int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_statx *stx, unsigned int want, unsigned int flags); +/** + * Get attributes of a file relative to a file descriptor + * + * @param cmount the ceph mount handle to use for performing the stat. + * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD) + * @param relpath to the file/directory to get statistics of + * @param stx the ceph_statx struct that will be filled in with the file's statistics. + * @param want bitfield of CEPH_STATX_* flags showing designed attributes + * @param flags bitfield that can be used to set AT_* modifier flags (only AT_NO_ATTR_SYNC and AT_SYMLINK_NOFOLLOW) + * @returns 0 on success or negative error code on failure. + */ +int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + struct ceph_statx *stx, unsigned int want, unsigned int flags); + /** * Get a file's extended statistics and attributes. * @@ -896,6 +969,19 @@ int ceph_lchmod(struct ceph_mount_info *cmount, const char *path, mode_t mode); */ int ceph_fchmod(struct ceph_mount_info *cmount, int fd, mode_t mode); +/** + * Change the mode bits (permissions) of a file relative to a file descriptor. + * + * @param cmount the ceph mount handle to use for performing the chown. + * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD) + * @param relpath the relpath of the file/directory to change the ownership of. + * @param mode the new permissions to set. + * @param flags bitfield that can be used to set AT_* modifier flags (AT_SYMLINK_NOFOLLOW) + * @returns 0 on success or negative error code on failure. + */ +int ceph_chmodat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + mode_t mode, int flags); + /** * Change the ownership of a file/directory. * @@ -929,6 +1015,20 @@ int ceph_fchown(struct ceph_mount_info *cmount, int fd, int uid, int gid); */ int ceph_lchown(struct ceph_mount_info *cmount, const char *path, int uid, int gid); +/** + * Change the ownership of a file/directory releative to a file descriptor. + * + * @param cmount the ceph mount handle to use for performing the chown. + * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD) + * @param relpath the relpath of the file/directory to change the ownership of. + * @param uid the user id to set on the file/directory. + * @param gid the group id to set on the file/directory. + * @param flags bitfield that can be used to set AT_* modifier flags (AT_SYMLINK_NOFOLLOW) + * @returns 0 on success or negative error code on failure. + */ +int ceph_chownat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + uid_t uid, gid_t gid, int flags); + /** * Change file/directory last access and modification times. * @@ -989,6 +1089,21 @@ int ceph_futimes(struct ceph_mount_info *cmount, int fd, struct timeval times[2] */ int ceph_futimens(struct ceph_mount_info *cmount, int fd, struct timespec times[2]); +/** + * Change file/directory last access and modification times relative + * to a file descriptor. + * + * @param cmount the ceph mount handle to use for performing the utime. + * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD) + * @param relpath the relpath of the file/directory to change the ownership of. + * @param dirfd the fd of the open file/directory to set the time values of. + * @param times holding the access and modification times to set on the file. + * @param flags bitfield that can be used to set AT_* modifier flags (AT_SYMLINK_NOFOLLOW) + * @returns 0 on success or negative error code on failure. + */ +int ceph_utimensat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + struct timespec times[2], int flags); + /** * Apply or remove an advisory lock. * @@ -1041,6 +1156,20 @@ int ceph_mknod(struct ceph_mount_info *cmount, const char *path, mode_t mode, de */ int ceph_open(struct ceph_mount_info *cmount, const char *path, int flags, mode_t mode); +/** + * Create and/or open a file relative to a directory + * + * @param cmount the ceph mount handle to use for performing the open. + * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD) + * @param relpath the path of the file to open. If the flags parameter includes O_CREAT, + * the file will first be created before opening. + * @param flags a set of option masks that control how the file is created/opened. + * @param mode the permissions to place on the file if the file does not exist and O_CREAT + * is specified in the flags. + * @returns a non-negative file descriptor number on success or a negative error code on failure. + */ +int ceph_openat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, int flags, mode_t mode); + /** * Create and/or open a file with a specific file layout. * diff --git a/src/include/fs_types.h b/src/include/fs_types.h index aa9b3e7d3bd..fc34e702a43 100644 --- a/src/include/fs_types.h +++ b/src/include/fs_types.h @@ -46,6 +46,10 @@ class JSONObj; #define CEPHFS_ETIME 62 #define CEPHFS_EOLDSNAPC 85 +// taken from linux kernel: include/uapi/linux/fcntl.h +#define CEPHFS_AT_FDCWD -100 /* Special value used to indicate + openat should use the current + working directory. */ // -------------------------------------- // ino diff --git a/src/libcephfs.cc b/src/libcephfs.cc index 8f88b387106..d9a348fd6b7 100644 --- a/src/libcephfs.cc +++ b/src/libcephfs.cc @@ -634,6 +634,14 @@ extern "C" int ceph_opendir(struct ceph_mount_info *cmount, return cmount->get_client()->opendir(name, (dir_result_t **)dirpp, cmount->default_perms); } +extern "C" int ceph_fdopendir(struct ceph_mount_info *cmount, int dirfd, + struct ceph_dir_result **dirpp) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->fdopendir(dirfd, (dir_result_t **)dirpp, cmount->default_perms); +} + extern "C" int ceph_closedir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp) { if (!cmount->is_mounted()) @@ -728,6 +736,13 @@ extern "C" int ceph_unlink(struct ceph_mount_info *cmount, const char *path) return cmount->get_client()->unlink(path, cmount->default_perms); } +extern "C" int ceph_unlinkat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, int flags) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->unlinkat(dirfd, relpath, flags, cmount->default_perms); +} + extern "C" int ceph_rename(struct ceph_mount_info *cmount, const char *from, const char *to) { @@ -744,6 +759,14 @@ extern "C" int ceph_mkdir(struct ceph_mount_info *cmount, const char *path, mode return cmount->get_client()->mkdir(path, mode, cmount->default_perms); } +extern "C" int ceph_mkdirat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + mode_t mode) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->mkdirat(dirfd, relpath, mode, cmount->default_perms); +} + extern "C" int ceph_mksnap(struct ceph_mount_info *cmount, const char *path, const char *name, mode_t mode, struct snap_metadata *snap_metadata, size_t nr_snap_metadata) { @@ -788,6 +811,14 @@ extern "C" int ceph_readlink(struct ceph_mount_info *cmount, const char *path, return cmount->get_client()->readlink(path, buf, size, cmount->default_perms); } +extern "C" int ceph_readlinkat(struct ceph_mount_info *cmount, int dirfd, + const char *relpath, char *buf, int64_t size) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->readlinkat(dirfd, relpath, buf, size, cmount->default_perms); +} + extern "C" int ceph_symlink(struct ceph_mount_info *cmount, const char *existing, const char *newname) { @@ -796,6 +827,14 @@ extern "C" int ceph_symlink(struct ceph_mount_info *cmount, const char *existing return cmount->get_client()->symlink(existing, newname, cmount->default_perms); } +extern "C" int ceph_symlinkat(struct ceph_mount_info *cmount, const char *existing, int dirfd, + const char *newname) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->symlinkat(existing, dirfd, newname, cmount->default_perms); +} + extern "C" int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_statx *stx, unsigned int want, unsigned int flags) { @@ -807,6 +846,17 @@ extern "C" int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_s want, flags); } +extern "C" int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + struct ceph_statx *stx, unsigned int want, unsigned int flags) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + if (flags & ~CEPH_REQ_FLAG_MASK) + return -EINVAL; + return cmount->get_client()->statxat(dirfd, relpath, stx, cmount->default_perms, + want, flags); +} + extern "C" int ceph_statx(struct ceph_mount_info *cmount, const char *path, struct ceph_statx *stx, unsigned int want, unsigned int flags) { @@ -964,6 +1014,14 @@ extern "C" int ceph_fchmod(struct ceph_mount_info *cmount, int fd, mode_t mode) return -ENOTCONN; return cmount->get_client()->fchmod(fd, mode, cmount->default_perms); } + +extern "C" int ceph_chmodat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + mode_t mode, int flags) { + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->chmodat(dirfd, relpath, mode, flags, cmount->default_perms); +} + extern "C" int ceph_chown(struct ceph_mount_info *cmount, const char *path, int uid, int gid) { @@ -986,6 +1044,12 @@ extern "C" int ceph_lchown(struct ceph_mount_info *cmount, const char *path, return cmount->get_client()->lchown(path, uid, gid, cmount->default_perms); } +extern "C" int ceph_chownat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + uid_t uid, gid_t gid, int flags) { + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->chownat(dirfd, relpath, uid, gid, flags, cmount->default_perms); +} extern "C" int ceph_utime(struct ceph_mount_info *cmount, const char *path, struct utimbuf *buf) @@ -1035,6 +1099,13 @@ extern "C" int ceph_futimens(struct ceph_mount_info *cmount, int fd, return cmount->get_client()->futimens(fd, times, cmount->default_perms); } +extern "C" int ceph_utimensat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + struct timespec times[2], int flags) { + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->utimensat(dirfd, relpath, times, flags, cmount->default_perms); +} + extern "C" int ceph_flock(struct ceph_mount_info *cmount, int fd, int operation, uint64_t owner) { @@ -1068,6 +1139,14 @@ extern "C" int ceph_open(struct ceph_mount_info *cmount, const char *path, return cmount->get_client()->open(path, flags, cmount->default_perms, mode); } +extern "C" int ceph_openat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, + int flags, mode_t mode) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->openat(dirfd, relpath, flags, cmount->default_perms, mode); +} + extern "C" int ceph_open_layout(struct ceph_mount_info *cmount, const char *path, int flags, mode_t mode, int stripe_unit, int stripe_count, int object_size, const char *data_pool) {