1/* 2 * Copyright (c) International Business Machines Corp., 2006 3 * Author: Yi Yang <yyangcdl@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 <stdlib.h> 45#include <errno.h> 46#include <string.h> 47#include <signal.h> 48#include <sys/mount.h> 49 50#include "test.h" 51#include "safe_macros.h" 52#include "lapi/fcntl.h" 53#include "lapi/renameat.h" 54 55#define MNTPOINT "mntpoint" 56#define TESTDIR "testdir" 57#define NEW_TESTDIR "new_testdir" 58#define TESTDIR2 "/loopdir" 59#define NEW_TESTDIR2 "newloopdir" 60#define TESTDIR3 "emlinkdir" 61#define NEW_TESTDIR3 "testemlinkdir/new_emlinkdir" 62#define TESTFILE "testfile" 63#define NEW_TESTFILE "new_testfile" 64#define TESTFILE2 "testfile2" 65#define NEW_TESTFILE2 "new_testfile2" 66#define TESTFILE3 "testdir/testfile" 67#define TESTFILE4 "testfile4" 68#define TESTFILE5 "mntpoint/rofile" 69#define NEW_TESTFILE5 "mntpoint/newrofile" 70 71#define DIRMODE (S_IRWXU | S_IRWXG | S_IRWXO) 72#define FILEMODE (S_IRWXU | S_IRWXG | S_IRWXO) 73 74static int curfd = AT_FDCWD; 75static int olddirfd; 76static int newdirfd; 77static int badfd = 100; 78static int filefd; 79static char absoldpath[256]; 80static char absnewpath[256]; 81static char looppathname[sizeof(TESTDIR2) * 43] = "."; 82static int max_subdirs; 83 84static int mount_flag; 85static const char *device; 86 87static struct test_case_t { 88 int *oldfdptr; 89 const char *oldpath; 90 int *newfdptr; 91 const char *newpath; 92 int exp_errno; 93} test_cases[] = { 94 { &curfd, TESTFILE, &curfd, NEW_TESTFILE, 0 }, 95 { &olddirfd, TESTFILE, &newdirfd, NEW_TESTFILE, 0 }, 96 { &olddirfd, absoldpath, &newdirfd, absnewpath, 0 }, 97 { &badfd, TESTFILE, &badfd, NEW_TESTFILE, EBADF }, 98 { &filefd, TESTFILE, &filefd, NEW_TESTFILE, ENOTDIR }, 99 { &curfd, looppathname, &curfd, NEW_TESTDIR2, ELOOP }, 100 { &curfd, TESTFILE5, &curfd, NEW_TESTFILE5, EROFS }, 101 { &curfd, TESTDIR3, &curfd, NEW_TESTDIR3, EMLINK }, 102}; 103 104static void setup(void); 105static void cleanup(void); 106static void renameat_verify(const struct test_case_t *); 107 108char *TCID = "renameat01"; 109int TST_TOTAL = ARRAY_SIZE(test_cases); 110 111int main(int ac, char **av) 112{ 113 int i, lc; 114 115 tst_parse_opts(ac, av, NULL, NULL); 116 117 setup(); 118 119 for (lc = 0; TEST_LOOPING(lc); lc++) { 120 tst_count = 0; 121 122 for (i = 0; i < TST_TOTAL; i++) 123 renameat_verify(&test_cases[i]); 124 } 125 126 cleanup(); 127 tst_exit(); 128} 129 130static void setup(void) 131{ 132 char *tmpdir; 133 const char *fs_type; 134 int i; 135 136 tst_require_root(); 137 138 tst_sig(NOFORK, DEF_HANDLER, cleanup); 139 140 tst_tmpdir(); 141 142 fs_type = tst_dev_fs_type(); 143 device = tst_acquire_device(cleanup); 144 145 if (!device) 146 tst_brkm(TCONF, cleanup, "Failed to obtain block device"); 147 148 TEST_PAUSE; 149 150 SAFE_TOUCH(cleanup, TESTFILE, FILEMODE, NULL); 151 152 SAFE_TOUCH(cleanup, TESTFILE2, FILEMODE, NULL); 153 tmpdir = tst_get_tmpdir(); 154 sprintf(absoldpath, "%s/%s", tmpdir, TESTFILE2); 155 sprintf(absnewpath, "%s/%s", tmpdir, NEW_TESTFILE2); 156 free(tmpdir); 157 158 SAFE_MKDIR(cleanup, TESTDIR, DIRMODE); 159 SAFE_TOUCH(cleanup, TESTFILE3, FILEMODE, NULL); 160 SAFE_MKDIR(cleanup, NEW_TESTDIR, DIRMODE); 161 162 olddirfd = SAFE_OPEN(cleanup, TESTDIR, O_DIRECTORY); 163 newdirfd = SAFE_OPEN(cleanup, NEW_TESTDIR, O_DIRECTORY); 164 165 filefd = SAFE_OPEN(cleanup, TESTFILE4, 166 O_RDWR | O_CREAT, FILEMODE); 167 168 /* 169 * NOTE: the ELOOP test is written based on that the 170 * consecutive symlinks limit in kernel is hardwired 171 * to 40. 172 */ 173 SAFE_MKDIR(cleanup, "loopdir", DIRMODE); 174 SAFE_SYMLINK(cleanup, "../loopdir", "loopdir/loopdir"); 175 for (i = 0; i < 43; i++) 176 strcat(looppathname, TESTDIR2); 177 178 tst_mkfs(cleanup, device, fs_type, NULL, NULL); 179 SAFE_MKDIR(cleanup, MNTPOINT, DIRMODE); 180 SAFE_MOUNT(cleanup, device, MNTPOINT, fs_type, 0, NULL); 181 mount_flag = 1; 182 SAFE_TOUCH(cleanup, TESTFILE5, FILEMODE, NULL); 183 SAFE_MOUNT(cleanup, device, MNTPOINT, fs_type, MS_REMOUNT | MS_RDONLY, 184 NULL); 185 186 SAFE_MKDIR(cleanup, TESTDIR3, DIRMODE); 187 max_subdirs = tst_fs_fill_subdirs(cleanup, "testemlinkdir"); 188} 189 190static void renameat_verify(const struct test_case_t *tc) 191{ 192 if (tc->exp_errno == EMLINK && max_subdirs == 0) { 193 tst_resm(TCONF, "EMLINK test is not appropriate"); 194 return; 195 } 196 197 TEST(renameat(*(tc->oldfdptr), tc->oldpath, 198 *(tc->newfdptr), tc->newpath)); 199 200 if (tc->exp_errno && TEST_RETURN != -1) { 201 tst_resm(TFAIL, "renameat() succeeded unexpectedly"); 202 return; 203 } 204 205 if (tc->exp_errno == 0 && TEST_RETURN != 0) { 206 tst_resm(TFAIL | TTERRNO, "renameat() failed unexpectedly"); 207 return; 208 } 209 210 if (TEST_ERRNO == tc->exp_errno) { 211 tst_resm(TPASS | TTERRNO, 212 "renameat() returned the expected value"); 213 } else { 214 tst_resm(TFAIL | TTERRNO, 215 "renameat() got unexpected return value; expected: " 216 "%d - %s", tc->exp_errno, 217 strerror(tc->exp_errno)); 218 } 219 220 if (TEST_ERRNO == 0 && renameat(*(tc->newfdptr), tc->newpath, 221 *(tc->oldfdptr), tc->oldpath) < 0) { 222 tst_brkm(TBROK | TERRNO, cleanup, "renameat(%d, %s, " 223 "%d, %s) failed.", *(tc->newfdptr), tc->newpath, 224 *(tc->oldfdptr), tc->oldpath); 225 } 226} 227 228static void cleanup(void) 229{ 230 if (olddirfd > 0 && close(olddirfd) < 0) 231 tst_resm(TWARN | TERRNO, "close olddirfd failed"); 232 233 if (newdirfd > 0 && close(newdirfd) < 0) 234 tst_resm(TWARN | TERRNO, "close newdirfd failed"); 235 236 if (filefd > 0 && close(filefd) < 0) 237 tst_resm(TWARN | TERRNO, "close filefd failed"); 238 239 if (mount_flag && tst_umount(MNTPOINT) < 0) 240 tst_resm(TWARN | TERRNO, "umount %s failed", MNTPOINT); 241 242 if (device) 243 tst_release_device(device); 244 245 tst_rmdir(); 246} 247