[fs][minfs][memfs] Add support for renaming to self

ZX-1729 #done

Change-Id: I6f780d6403b5e0bc97c8f3d33659acd1f81d4ea8
diff --git a/system/ulib/memfs/directory.cpp b/system/ulib/memfs/directory.cpp
index 12f94b2..81d8714 100644
--- a/system/ulib/memfs/directory.cpp
+++ b/system/ulib/memfs/directory.cpp
@@ -162,6 +162,10 @@
 
     if (!olddn->IsDirectory() && (src_must_be_dir || dst_must_be_dir)) {
         return ZX_ERR_NOT_DIR;
+    } else if ((newdir->ino() == ino_) && (oldname == newname)) {
+        // Renaming a file or directory to itself?
+        // Shortcut success case
+        return ZX_OK;
     }
 
     // Verify that the destination is not a subdirectory of the source (if
diff --git a/system/ulib/memfs/include/memfs/vnode.h b/system/ulib/memfs/include/memfs/vnode.h
index 607f8be..ed84ae4 100644
--- a/system/ulib/memfs/include/memfs/vnode.h
+++ b/system/ulib/memfs/include/memfs/vnode.h
@@ -46,6 +46,7 @@
     virtual ~VnodeMemfs();
 
     Vfs* vfs() const { return vfs_; }
+    uint64_t ino() const { return ino_; }
 
     fbl::RefPtr<Dnode> dnode_;
     uint32_t link_count_;
diff --git a/system/ulib/minfs/vnode.cpp b/system/ulib/minfs/vnode.cpp
index 22b164c..fef0acf 100644
--- a/system/ulib/minfs/vnode.cpp
+++ b/system/ulib/minfs/vnode.cpp
@@ -1824,6 +1824,10 @@
     // If either the 'src' or 'dst' must be directories, BOTH of them must be directories.
     if (!oldvn->IsDirectory() && (src_must_be_dir || dst_must_be_dir)) {
         return ZX_ERR_NOT_DIR;
+    } else if ((newdir->ino_ == ino_) && (oldname == newname)) {
+        // Renaming a file or directory to itself?
+        // Shortcut success case.
+        return ZX_OK;
     }
 
     // if the entry for 'newname' exists, make sure it can be replaced by
diff --git a/system/utest/fs/test-rename.c b/system/utest/fs/test-rename.c
index f2326b0..3617794 100644
--- a/system/utest/fs/test-rename.c
+++ b/system/utest/fs/test-rename.c
@@ -22,9 +22,14 @@
     // Cannot rename when src does not exist
     ASSERT_EQ(rename("::alpha", "::bravo"), -1, "");
 
-    // Cannot rename to self
+    // Renaming to self is fine
     ASSERT_EQ(mkdir("::alpha", 0755), 0, "");
-    ASSERT_EQ(rename("::alpha", "::alpha"), -1, "");
+    ASSERT_EQ(rename("::alpha", "::alpha"), 0, "");
+    ASSERT_EQ(rename("::alpha/.", "::alpha/."), 0, "");
+    ASSERT_EQ(rename("::alpha/", "::alpha"), 0, "");
+    ASSERT_EQ(rename("::alpha", "::alpha/"), 0, "");
+    ASSERT_EQ(rename("::alpha/", "::alpha/"), 0, "");
+    ASSERT_EQ(rename("::alpha/./../alpha", "::alpha/./../alpha"), 0, "");
 
     // Cannot rename dir to file
     int fd = open("::bravo", O_RDWR | O_CREAT | O_EXCL, 0644);
@@ -43,6 +48,12 @@
     fd = open("::alpha/charlie", O_RDWR | O_CREAT | O_EXCL, 0644);
     ASSERT_GT(fd, 0, "");
     ASSERT_EQ(rename("::alpha/charlie", "::alpha/delta"), 0, "");
+    // File rename to self
+    ASSERT_EQ(rename("::alpha/delta", "::alpha/delta"), 0, "");
+    // Not permitted with trailing '/'
+    ASSERT_EQ(rename("::alpha/delta", "::alpha/delta/"), -1, "");
+    ASSERT_EQ(rename("::alpha/delta/", "::alpha/delta"), -1, "");
+    ASSERT_EQ(rename("::alpha/delta/", "::alpha/delta/"), -1, "");
     ASSERT_EQ(close(fd), 0, "");
 
     // Rename file (dst does not exist)