]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
libcephfs: introduce basic *at() calls
authorVenky Shankar <vshankar@redhat.com>
Mon, 12 Apr 2021 09:25:34 +0000 (05:25 -0400)
committerVenky Shankar <vshankar@redhat.com>
Mon, 26 Apr 2021 13:14:27 +0000 (09:14 -0400)
Signed-off-by: Venky Shankar <vshankar@redhat.com>
src/client/Client.cc
src/client/Client.h
src/include/cephfs/libcephfs.h
src/include/fs_types.h
src/libcephfs.cc

index a99e1cd6ad87af2313e826920acef29f6ac6d3a1..f598600fc5c95e3fe8a36e7e0544f96108cb8157 100644 (file)
@@ -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<string>& 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<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) {
+  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,
index 89a0694124ece98251930c5c64b0376289e5e1ce..13fc2609ca899c1c090cfece13bbbd58189081a6 100644 (file)
@@ -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<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 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<MClientReply>& 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);
index d8042f5c5fee7ec99c3b3edf5691f4a4e497f78f..f37114351049bd55fb72c6f57f7dd2639a18d092 100644 (file)
@@ -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.
  *
index aa9b3e7d3bdd666969c52873f7ffa89e3d76f0ff..fc34e702a4341d15fa0bc23aed9fc3943b9a952f 100644 (file)
@@ -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
index 8f88b387106c6446e507e18a0c2be6b006f6b733..d9a348fd6b7909274f22f225bb6174a3a1e9e2ae 100644 (file)
@@ -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)
 {