1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <dirent.h> 6#include <errno.h> 7#include <fcntl.h> 8#include <limits.h> 9#include <stdio.h> 10#include <stdlib.h> 11#include <sys/stat.h> 12#include <sys/types.h> 13#include <unistd.h> 14 15#include <zircon/compiler.h> 16 17#include "filesystems.h" 18#include "misc.h" 19 20bool test_rename_basic(void) { 21 BEGIN_TEST; 22 // Cannot rename when src does not exist 23 ASSERT_EQ(rename("::alpha", "::bravo"), -1, ""); 24 25 // Renaming to self is fine 26 ASSERT_EQ(mkdir("::alpha", 0755), 0, ""); 27 ASSERT_EQ(rename("::alpha", "::alpha"), 0, ""); 28 ASSERT_EQ(rename("::alpha/.", "::alpha/."), 0, ""); 29 ASSERT_EQ(rename("::alpha/", "::alpha"), 0, ""); 30 ASSERT_EQ(rename("::alpha", "::alpha/"), 0, ""); 31 ASSERT_EQ(rename("::alpha/", "::alpha/"), 0, ""); 32 ASSERT_EQ(rename("::alpha/./../alpha", "::alpha/./../alpha"), 0, ""); 33 34 // Cannot rename dir to file 35 int fd = open("::bravo", O_RDWR | O_CREAT | O_EXCL, 0644); 36 ASSERT_GT(fd, 0, ""); 37 ASSERT_EQ(close(fd), 0, ""); 38 ASSERT_EQ(rename("::alpha", "::bravo"), -1, ""); 39 ASSERT_EQ(unlink("::bravo"), 0, ""); 40 41 // Rename dir (dst does not exist) 42 ASSERT_EQ(rename("::alpha", "::bravo"), 0, ""); 43 ASSERT_EQ(mkdir("::alpha", 0755), 0, ""); 44 // Rename dir (dst does exist) 45 ASSERT_EQ(rename("::bravo", "::alpha"), 0, ""); 46 47 // Rename file (dst does not exist) 48 fd = open("::alpha/charlie", O_RDWR | O_CREAT | O_EXCL, 0644); 49 ASSERT_GT(fd, 0, ""); 50 ASSERT_EQ(rename("::alpha/charlie", "::alpha/delta"), 0, ""); 51 // File rename to self 52 ASSERT_EQ(rename("::alpha/delta", "::alpha/delta"), 0, ""); 53 // Not permitted with trailing '/' 54 ASSERT_EQ(rename("::alpha/delta", "::alpha/delta/"), -1, ""); 55 ASSERT_EQ(rename("::alpha/delta/", "::alpha/delta"), -1, ""); 56 ASSERT_EQ(rename("::alpha/delta/", "::alpha/delta/"), -1, ""); 57 ASSERT_EQ(close(fd), 0, ""); 58 59 // Rename file (dst does not exist) 60 fd = open("::alpha/charlie", O_RDWR | O_CREAT | O_EXCL, 0644); 61 ASSERT_GT(fd, 0, ""); 62 ASSERT_EQ(rename("::alpha/delta", "::alpha/charlie"), 0, ""); 63 ASSERT_EQ(close(fd), 0, ""); 64 65 // Rename to different directory 66 ASSERT_EQ(mkdir("::bravo", 0755), 0, ""); 67 ASSERT_EQ(rename("::alpha/charlie", "::charlie"), 0, ""); 68 ASSERT_EQ(rename("::charlie", "::alpha/charlie"), 0, ""); 69 ASSERT_EQ(rename("::bravo", "::alpha/bravo"), 0, ""); 70 ASSERT_EQ(rename("::alpha/charlie", "::alpha/bravo/charlie"), 0, ""); 71 72 // Cannot rename directory to subdirectory of itself 73 ASSERT_EQ(rename("::alpha", "::alpha/bravo"), -1, ""); 74 ASSERT_EQ(rename("::alpha", "::alpha/bravo/charlie"), -1, ""); 75 ASSERT_EQ(rename("::alpha", "::alpha/bravo/charlie/delta"), -1, ""); 76 ASSERT_EQ(rename("::alpha", "::alpha/delta"), -1, ""); 77 ASSERT_EQ(rename("::alpha/bravo", "::alpha/bravo/charlie"), -1, ""); 78 ASSERT_EQ(rename("::alpha/bravo", "::alpha/bravo/charlie/delta"), -1, ""); 79 // Cannot rename to non-empty directory 80 ASSERT_EQ(rename("::alpha/bravo/charlie", "::alpha/bravo"), -1, ""); 81 ASSERT_EQ(rename("::alpha/bravo/charlie", "::alpha"), -1, ""); 82 ASSERT_EQ(rename("::alpha/bravo", "::alpha"), -1, ""); 83 84 // Clean up 85 ASSERT_EQ(unlink("::alpha/bravo/charlie"), 0, ""); 86 ASSERT_EQ(unlink("::alpha/bravo"), 0, ""); 87 ASSERT_EQ(unlink("::alpha"), 0, ""); 88 89 END_TEST; 90} 91 92bool test_rename_with_children(void) { 93 BEGIN_TEST; 94 95 ASSERT_EQ(mkdir("::dir_before_move", 0755), 0, ""); 96 ASSERT_EQ(mkdir("::dir_before_move/dir1", 0755), 0, ""); 97 ASSERT_EQ(mkdir("::dir_before_move/dir2", 0755), 0, ""); 98 ASSERT_EQ(mkdir("::dir_before_move/dir2/subdir", 0755), 0, ""); 99 int fd = open("::dir_before_move/file", O_RDWR | O_CREAT, 0644); 100 ASSERT_GT(fd, 0, ""); 101 102 const char file_contents[] = "This should be in the file"; 103 ASSERT_STREAM_ALL(write, fd, (uint8_t*) file_contents, strlen(file_contents)); 104 105 ASSERT_EQ(rename("::dir_before_move", "::dir"), 0, "Could not rename"); 106 107 // Check that the directory layout has persisted across rename 108 expected_dirent_t dir_contents[] = { 109 {false, ".", DT_DIR}, 110 {false, "dir1", DT_DIR}, 111 {false, "dir2", DT_DIR}, 112 {false, "file", DT_REG}, 113 }; 114 ASSERT_TRUE(check_dir_contents("::dir", dir_contents, countof(dir_contents)), ""); 115 expected_dirent_t dir2_contents[] = { 116 {false, ".", DT_DIR}, 117 {false, "subdir", DT_DIR}, 118 }; 119 ASSERT_TRUE(check_dir_contents("::dir/dir2", dir2_contents, countof(dir2_contents)), ""); 120 121 // Check the our file data has lasted (without re-opening) 122 ASSERT_TRUE(check_file_contents(fd, (uint8_t*) file_contents, strlen(file_contents)), ""); 123 124 // Check the our file data has lasted (with re-opening) 125 ASSERT_EQ(close(fd), 0, ""); 126 fd = open("::dir/file", O_RDONLY, 06444); 127 ASSERT_GT(fd, 0, ""); 128 ASSERT_TRUE(check_file_contents(fd, (uint8_t*) file_contents, strlen(file_contents)), ""); 129 ASSERT_EQ(close(fd), 0, ""); 130 131 // Clean up 132 ASSERT_EQ(unlink("::dir/dir1"), 0, ""); 133 ASSERT_EQ(unlink("::dir/dir2/subdir"), 0, ""); 134 ASSERT_EQ(unlink("::dir/dir2"), 0, ""); 135 ASSERT_EQ(unlink("::dir/file"), 0, ""); 136 ASSERT_EQ(unlink("::dir"), 0, ""); 137 138 END_TEST; 139} 140 141bool test_rename_absolute_relative(void) { 142 BEGIN_TEST; 143 144 char cwd[PATH_MAX]; 145 ASSERT_NONNULL(getcwd(cwd, sizeof(cwd)), ""); 146 147 // Change the cwd to a known directory 148 ASSERT_EQ(mkdir("::working_dir", 0755), 0, ""); 149 DIR* dir = opendir("::working_dir"); 150 ASSERT_NONNULL(dir, ""); 151 ASSERT_EQ(chdir("::working_dir"), 0, ""); 152 153 // Make a "foo" directory in the cwd 154 int fd = dirfd(dir); 155 ASSERT_NE(fd, -1, ""); 156 ASSERT_EQ(mkdirat(fd, "foo", 0755), 0, ""); 157 expected_dirent_t dir_contents_foo[] = { 158 {false, ".", DT_DIR}, 159 {false, "foo", DT_DIR}, 160 }; 161 ASSERT_TRUE(fcheck_dir_contents(dir, dir_contents_foo, countof(dir_contents_foo)), ""); 162 163 // Rename "foo" to "bar" using mixed paths 164 ASSERT_EQ(rename("::working_dir/foo", "bar"), 0, "Could not rename foo to bar"); 165 expected_dirent_t dir_contents_bar[] = { 166 {false, ".", DT_DIR}, 167 {false, "bar", DT_DIR}, 168 }; 169 ASSERT_TRUE(fcheck_dir_contents(dir, dir_contents_bar, countof(dir_contents_bar)), ""); 170 171 // Rename "bar" back to "foo" using mixed paths in the other direction 172 ASSERT_EQ(rename("bar", "::working_dir/foo"), 0, "Could not rename bar to foo"); 173 ASSERT_TRUE(fcheck_dir_contents(dir, dir_contents_foo, countof(dir_contents_foo)), ""); 174 175 ASSERT_EQ(rmdir("::working_dir/foo"), 0, ""); 176 177 // Change the cwd back to the original, whatever it was before 178 // this test started 179 ASSERT_EQ(chdir(cwd), 0, "Could not return to original cwd"); 180 181 ASSERT_EQ(rmdir("::working_dir"), 0, ""); 182 ASSERT_EQ(closedir(dir), 0, ""); 183 184 END_TEST; 185} 186 187bool test_rename_at(void) { 188 BEGIN_TEST; 189 190 ASSERT_EQ(mkdir("::foo", 0755), 0, ""); 191 ASSERT_EQ(mkdir("::foo/baz", 0755), 0, ""); 192 ASSERT_EQ(mkdir("::bar", 0755), 0, ""); 193 194 // Normal case of renameat, from one directory to another 195 int foofd = open("::foo", O_RDONLY | O_DIRECTORY, 0644); 196 ASSERT_GT(foofd, 0, ""); 197 int barfd = open("::bar", O_RDONLY | O_DIRECTORY, 0644); 198 ASSERT_GT(barfd, 0, ""); 199 200 ASSERT_EQ(renameat(foofd, "baz", barfd, "zab"), 0, ""); 201 202 expected_dirent_t empty_contents[] = { 203 {false, ".", DT_DIR}, 204 }; 205 ASSERT_TRUE(check_dir_contents("::foo", empty_contents, countof(empty_contents)), ""); 206 expected_dirent_t contains_zab[] = { 207 {false, ".", DT_DIR}, 208 {false, "zab", DT_DIR}, 209 }; 210 ASSERT_TRUE(check_dir_contents("::bar", contains_zab, countof(contains_zab)), ""); 211 212 // Alternate case of renameat, where an absolute path ignores 213 // the file descriptor. 214 // 215 // Here, barfd is used (in the first argument) but ignored (in the second argument). 216 ASSERT_EQ(renameat(barfd, "zab", barfd, "::foo/baz"), 0, ""); 217 expected_dirent_t contains_baz[] = { 218 {false, ".", DT_DIR}, 219 {false, "baz", DT_DIR}, 220 }; 221 ASSERT_TRUE(check_dir_contents("::foo", contains_baz, countof(contains_baz)), ""); 222 ASSERT_TRUE(check_dir_contents("::bar", empty_contents, countof(empty_contents)), ""); 223 224 // The 'absolute-path-ignores-fd' case should also work with invalid fds. 225 ASSERT_EQ(renameat(-1, "::foo/baz", -1, "::bar/baz"), 0, ""); 226 ASSERT_TRUE(check_dir_contents("::foo", empty_contents, countof(empty_contents)), ""); 227 ASSERT_TRUE(check_dir_contents("::bar", contains_baz, countof(contains_baz)), ""); 228 229 // However, relative paths should not be allowed with invalid fds. 230 ASSERT_EQ(renameat(-1, "baz", foofd, "baz"), -1, ""); 231 ASSERT_EQ(errno, EBADF, ""); 232 233 // Additionally, we shouldn't be able to renameat to a file. 234 int fd = openat(barfd, "filename", O_CREAT | O_RDWR | O_EXCL); 235 ASSERT_GT(fd, 0, ""); 236 ASSERT_EQ(renameat(foofd, "baz", fd, "baz"), -1, ""); 237 // NOTE: not checking for "ENOTDIR", since ENOTSUPPORTED might be returned instead. 238 239 // Clean up 240 ASSERT_EQ(close(fd), 0, ""); 241 ASSERT_EQ(unlink("::bar/filename"), 0, ""); 242 ASSERT_EQ(rmdir("::bar/baz"), 0, ""); 243 ASSERT_EQ(close(foofd), 0, ""); 244 ASSERT_EQ(close(barfd), 0, ""); 245 ASSERT_EQ(rmdir("::foo"), 0, ""); 246 ASSERT_EQ(rmdir("::bar"), 0, ""); 247 END_TEST; 248} 249 250RUN_FOR_ALL_FILESYSTEMS(rename_tests, 251 RUN_TEST_MEDIUM(test_rename_basic) 252 RUN_TEST_MEDIUM(test_rename_with_children) 253 RUN_TEST_MEDIUM(test_rename_absolute_relative) 254 RUN_TEST_MEDIUM(test_rename_at) 255) 256