From 85cfa4f1d384b7bbbd811031b7013099aaafe706 Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 3 Mar 2016 11:59:21 +0000 Subject: [PATCH] libcephfs: fix crash on getpwd ...if the directory has been unlinked in the background. Previously this would assert out, now it will just give you the last string that you passed into chdir. Signed-off-by: John Spray --- src/client/Client.cc | 12 +++++++++++- src/client/Client.h | 2 +- src/client/SyntheticClient.cc | 5 ++++- src/libcephfs.cc | 7 ++++++- src/test/pybind/test_cephfs.py | 12 ++++++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/client/Client.cc b/src/client/Client.cc index 53984d29059..206e1dd1eb3 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -8663,7 +8663,7 @@ int Client::fstat(int fd, struct stat *stbuf) // not written yet, but i want to link! -int Client::chdir(const char *relpath) +int Client::chdir(const char *relpath, std::string &new_cwd) { Mutex::Locker lock(client_lock); tout(cct) << "chdir" << std::endl; @@ -8676,6 +8676,8 @@ int Client::chdir(const char *relpath) if (cwd != in) cwd.swap(in); ldout(cct, 3) << "chdir(" << relpath << ") cwd now " << cwd->ino << dendl; + + getcwd(new_cwd); return 0; } @@ -8687,7 +8689,15 @@ void Client::getcwd(string& dir) Inode *in = cwd.get(); while (in != root) { assert(in->dn_set.size() < 2); // dirs can't be hard-linked + + // A cwd or ancester is unlinked + if (in->dn_set.empty()) { + return; + } + Dentry *dn = in->get_first_parent(); + + if (!dn) { // look it up ldout(cct, 10) << "getcwd looking up parent for " << *in << dendl; diff --git a/src/client/Client.h b/src/client/Client.h index 67b5c5e9c81..d53ca1df169 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -892,7 +892,7 @@ public: int statfs(const char *path, struct statvfs *stbuf); // crap - int chdir(const char *s); + int chdir(const char *s, std::string &new_cwd); void getcwd(std::string& cwd); // namespace ops diff --git a/src/client/SyntheticClient.cc b/src/client/SyntheticClient.cc index 78fd7ce5120..b2ef93ae30b 100644 --- a/src/client/SyntheticClient.cc +++ b/src/client/SyntheticClient.cc @@ -1206,7 +1206,10 @@ int SyntheticClient::play_trace(Trace& t, string& prefix, bool metadata_only) client->fsync(fd, b); } else if (strcmp(op, "chdir") == 0) { const char *a = t.get_string(buf, p); - client->chdir(a); + // Client users should remember their path, but since this + // is just a synthetic client we ignore it. + std::string ignore; + client->chdir(a, ignore); } else if (strcmp(op, "statfs") == 0) { struct statvfs stbuf; client->statfs("/", &stbuf); diff --git a/src/libcephfs.cc b/src/libcephfs.cc index 037a018a595..b01cf227abb 100644 --- a/src/libcephfs.cc +++ b/src/libcephfs.cc @@ -231,6 +231,11 @@ public: return cwd.c_str(); } + int chdir(const char *to) + { + return client->chdir(to, cwd); + } + CephContext *get_ceph_context() const { return cct; } @@ -454,7 +459,7 @@ extern "C" int ceph_chdir (struct ceph_mount_info *cmount, const char *s) { if (!cmount->is_mounted()) return -ENOTCONN; - return cmount->get_client()->chdir(s); + return cmount->chdir(s); } extern "C" int ceph_opendir(struct ceph_mount_info *cmount, diff --git a/src/test/pybind/test_cephfs.py b/src/test/pybind/test_cephfs.py index d2f3eb64059..77cd31bf994 100644 --- a/src/test/pybind/test_cephfs.py +++ b/src/test/pybind/test_cephfs.py @@ -143,3 +143,15 @@ def test_symlink(): cephfs.close(fd) cephfs.unlink('file-2') +@with_setup(setup_test) +def test_delete_cwd(): + assert_equal("/", cephfs.getcwd()) + + cephfs.mkdir("/temp-directory", 0755) + cephfs.chdir("/temp-directory") + cephfs.rmdir("/temp-directory") + + # getcwd gives you something stale here: it remembers the path string + # even when things are unlinked. It's up to the caller to find out + # whether it really still exists + assert_equal("/temp-directory", cephfs.getcwd()) -- 2.47.3