1 /* 2 * Copyright (c) International Business Machines Corp., 2006 3 * Author: Yi Yang <yyangcdl (at) cn.ibm.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 /* 20 * Description: 21 * Verify that, 22 * 1) renameat(2) returns -1 and sets errno to EBADF if olddirfd 23 * or newdirfd is not a valid file descriptor. 24 * 2) renameat(2) returns -1 and sets errno to ENOTDIR if oldpath 25 * is relative and olddirfd is a file descriptor referring to 26 * a file other than a directory, or similar for newpath and 27 * newdirfd. 28 * 3) renameat(2) returns -1 and sets errno to ELOOP if too many 29 * symbolic links were encountered in resolving oldpath or 30 * newpath. 31 * 4) renameat(2) returns -1 and sets errno to EROFS if the file 32 * is on a read-only file system. 33 * 5) renameat(2) returns -1 and sets errno to EMLINK if oldpath 34 * already has the maximum number of links to it, or it is a 35 * directory and the directory containing newpath has the 36 * maximum number of links. 37 */ 38 39 #define _GNU_SOURCE 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/time.h> 44 #include <fcntl.h> 45 #include <error.h> 46 #include <stdlib.h> 47 #include <errno.h> 48 #include <string.h> 49 #include <signal.h> 50 #include <sys/mount.h> 51 52 #include "test.h" 53 #include "safe_macros.h" 54 #include "lapi/fcntl.h" 55 #include "lapi/renameat.h" 56 57 #define MNTPOINT "mntpoint" 58 #define TESTDIR "testdir" 59 #define NEW_TESTDIR "new_testdir" 60 #define TESTDIR2 "/loopdir" 61 #define NEW_TESTDIR2 "newloopdir" 62 #define TESTDIR3 "emlinkdir" 63 #define NEW_TESTDIR3 "testemlinkdir/new_emlinkdir" 64 #define TESTFILE "testfile" 65 #define NEW_TESTFILE "new_testfile" 66 #define TESTFILE2 "testfile2" 67 #define NEW_TESTFILE2 "new_testfile2" 68 #define TESTFILE3 "testdir/testfile" 69 #define TESTFILE4 "testfile4" 70 #define TESTFILE5 "mntpoint/rofile" 71 #define NEW_TESTFILE5 "mntpoint/newrofile" 72 73 #define DIRMODE (S_IRWXU | S_IRWXG | S_IRWXO) 74 #define FILEMODE (S_IRWXU | S_IRWXG | S_IRWXO) 75 76 static int curfd = AT_FDCWD; 77 static int olddirfd; 78 static int newdirfd; 79 static int badfd = 100; 80 static int filefd; 81 static char absoldpath[256]; 82 static char absnewpath[256]; 83 static char looppathname[sizeof(TESTDIR2) * 43] = "."; 84 static int max_subdirs; 85 86 static int mount_flag; 87 static const char *device; 88 89 static struct test_case_t { 90 int *oldfdptr; 91 const char *oldpath; 92 int *newfdptr; 93 const char *newpath; 94 int exp_errno; 95 } test_cases[] = { 96 { &curfd, TESTFILE, &curfd, NEW_TESTFILE, 0 }, 97 { &olddirfd, TESTFILE, &newdirfd, NEW_TESTFILE, 0 }, 98 { &olddirfd, absoldpath, &newdirfd, absnewpath, 0 }, 99 { &badfd, TESTFILE, &badfd, NEW_TESTFILE, EBADF }, 100 { &filefd, TESTFILE, &filefd, NEW_TESTFILE, ENOTDIR }, 101 { &curfd, looppathname, &curfd, NEW_TESTDIR2, ELOOP }, 102 { &curfd, TESTFILE5, &curfd, NEW_TESTFILE5, EROFS }, 103 { &curfd, TESTDIR3, &curfd, NEW_TESTDIR3, EMLINK }, 104 }; 105 106 static void setup(void); 107 static void cleanup(void); 108 static void renameat_verify(const struct test_case_t *); 109 110 char *TCID = "renameat01"; 111 int TST_TOTAL = ARRAY_SIZE(test_cases); 112 113 int main(int ac, char **av) 114 { 115 int i, lc; 116 117 tst_parse_opts(ac, av, NULL, NULL); 118 119 setup(); 120 121 for (lc = 0; TEST_LOOPING(lc); lc++) { 122 tst_count = 0; 123 124 for (i = 0; i < TST_TOTAL; i++) 125 renameat_verify(&test_cases[i]); 126 } 127 128 cleanup(); 129 tst_exit(); 130 } 131 132 static void setup(void) 133 { 134 char *tmpdir; 135 const char *fs_type; 136 int i; 137 138 if ((tst_kvercmp(2, 6, 16)) < 0) { 139 tst_brkm(TCONF, NULL, 140 "This test can only run on kernels that are " 141 "2.6.16 and higher"); 142 } 143 144 tst_require_root(); 145 146 tst_sig(NOFORK, DEF_HANDLER, cleanup); 147 148 tst_tmpdir(); 149 150 fs_type = tst_dev_fs_type(); 151 device = tst_acquire_device(cleanup); 152 153 if (!device) 154 tst_brkm(TCONF, cleanup, "Failed to obtain block device"); 155 156 TEST_PAUSE; 157 158 SAFE_TOUCH(cleanup, TESTFILE, FILEMODE, NULL); 159 160 SAFE_TOUCH(cleanup, TESTFILE2, FILEMODE, NULL); 161 tmpdir = tst_get_tmpdir(); 162 sprintf(absoldpath, "%s/%s", tmpdir, TESTFILE2); 163 sprintf(absnewpath, "%s/%s", tmpdir, NEW_TESTFILE2); 164 free(tmpdir); 165 166 SAFE_MKDIR(cleanup, TESTDIR, DIRMODE); 167 SAFE_TOUCH(cleanup, TESTFILE3, FILEMODE, NULL); 168 SAFE_MKDIR(cleanup, NEW_TESTDIR, DIRMODE); 169 170 olddirfd = SAFE_OPEN(cleanup, TESTDIR, O_DIRECTORY); 171 newdirfd = SAFE_OPEN(cleanup, NEW_TESTDIR, O_DIRECTORY); 172 173 filefd = SAFE_OPEN(cleanup, TESTFILE4, 174 O_RDWR | O_CREAT, FILEMODE); 175 176 /* 177 * NOTE: the ELOOP test is written based on that the 178 * consecutive symlinks limit in kernel is hardwired 179 * to 40. 180 */ 181 SAFE_MKDIR(cleanup, "loopdir", DIRMODE); 182 SAFE_SYMLINK(cleanup, "../loopdir", "loopdir/loopdir"); 183 for (i = 0; i < 43; i++) 184 strcat(looppathname, TESTDIR2); 185 186 tst_mkfs(cleanup, device, fs_type, NULL, NULL); 187 SAFE_MKDIR(cleanup, MNTPOINT, DIRMODE); 188 if (mount(device, MNTPOINT, fs_type, 0, NULL) < 0) { 189 tst_brkm(TBROK | TERRNO, cleanup, 190 "mount device:%s failed", device); 191 } 192 mount_flag = 1; 193 SAFE_TOUCH(cleanup, TESTFILE5, FILEMODE, NULL); 194 if (mount(device, MNTPOINT, fs_type, 195 MS_REMOUNT | MS_RDONLY, NULL) < 0) { 196 tst_brkm(TBROK | TERRNO, cleanup, 197 "mount device:%s failed", device); 198 } 199 200 SAFE_MKDIR(cleanup, TESTDIR3, DIRMODE); 201 max_subdirs = tst_fs_fill_subdirs(cleanup, "testemlinkdir"); 202 } 203 204 static void renameat_verify(const struct test_case_t *tc) 205 { 206 if (tc->exp_errno == EMLINK && max_subdirs == 0) { 207 tst_resm(TCONF, "EMLINK test is not appropriate"); 208 return; 209 } 210 211 TEST(renameat(*(tc->oldfdptr), tc->oldpath, 212 *(tc->newfdptr), tc->newpath)); 213 214 if (tc->exp_errno && TEST_RETURN != -1) { 215 tst_resm(TFAIL, "renameat() succeeded unexpectedly"); 216 return; 217 } 218 219 if (tc->exp_errno == 0 && TEST_RETURN != 0) { 220 tst_resm(TFAIL | TTERRNO, "renameat() failed unexpectedly"); 221 return; 222 } 223 224 if (TEST_ERRNO == tc->exp_errno) { 225 tst_resm(TPASS | TTERRNO, 226 "renameat() returned the expected value"); 227 } else { 228 tst_resm(TFAIL | TTERRNO, 229 "renameat() got unexpected return value; expected: " 230 "%d - %s", tc->exp_errno, 231 strerror(tc->exp_errno)); 232 } 233 234 if (TEST_ERRNO == 0 && renameat(*(tc->newfdptr), tc->newpath, 235 *(tc->oldfdptr), tc->oldpath) < 0) { 236 tst_brkm(TBROK | TERRNO, cleanup, "renameat(%d, %s, " 237 "%d, %s) failed.", *(tc->newfdptr), tc->newpath, 238 *(tc->oldfdptr), tc->oldpath); 239 } 240 } 241 242 static void cleanup(void) 243 { 244 if (olddirfd > 0 && close(olddirfd) < 0) 245 tst_resm(TWARN | TERRNO, "close olddirfd failed"); 246 247 if (newdirfd > 0 && close(newdirfd) < 0) 248 tst_resm(TWARN | TERRNO, "close newdirfd failed"); 249 250 if (filefd > 0 && close(filefd) < 0) 251 tst_resm(TWARN | TERRNO, "close filefd failed"); 252 253 if (mount_flag && tst_umount(MNTPOINT) < 0) 254 tst_resm(TWARN | TERRNO, "umount %s failed", MNTPOINT); 255 256 if (device) 257 tst_release_device(device); 258 259 tst_rmdir(); 260 } 261