1 /****************************************************************************** 2 * 3 * Copyright (c) International Business Machines Corp., 2006 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 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 * NAME 20 * linkat01.c 21 * 22 * DESCRIPTION 23 * This test case will verify basic function of linkat 24 * added by kernel 2.6.16 or up. 25 * 26 * USAGE: <for command-line> 27 * linkat01 [-c n] [-e] [-i n] [-I x] [-P x] [-t] [-p] 28 * where: 29 * -c n : Run n copies simultaneously. 30 * -e : Turn on errno logging. 31 * -i n : Execute test n times. 32 * -I x : Execute test for x seconds. 33 * -p : Pause for SIGUSR1 before starting 34 * -P x : Pause for x seconds between iterations. 35 * -t : Turn on syscall timing. 36 * 37 * Author 38 * Yi Yang <yyangcdl (at) cn.ibm.com> 39 * 40 * History 41 * 08/25/2006 Created first by Yi Yang <yyangcdl (at) cn.ibm.com> 42 * 43 *****************************************************************************/ 44 45 #define _GNU_SOURCE 46 47 #include <sys/types.h> 48 #include <sys/stat.h> 49 #include <sys/time.h> 50 #include <fcntl.h> 51 #include <unistd.h> 52 #include <stdlib.h> 53 #include <errno.h> 54 #include <string.h> 55 #include <signal.h> 56 #include <inttypes.h> 57 #include <limits.h> 58 #include "test.h" 59 #include "lapi/syscalls.h" 60 #include "safe_macros.h" 61 62 #ifndef AT_FDCWD 63 #define AT_FDCWD -100 64 #endif 65 #ifndef AT_SYMLINK_FOLLOW 66 #define AT_SYMLINK_FOLLOW 0x400 67 #endif 68 69 struct test_struct; 70 static void setup(); 71 static void cleanup(); 72 static void setup_every_copy(); 73 static void mylinkat_test(struct test_struct *desc); 74 75 #define TEST_DIR1 "olddir" 76 #define TEST_DIR2 "newdir" 77 #define TEST_DIR3 "deldir" 78 #define TEST_FILE1 "oldfile" 79 #define TEST_FILE2 "newfile" 80 #define TEST_FIFO "fifo" 81 82 #define DPATHNAME_FMT "%s/" TEST_DIR2 "/" TEST_FILE1 83 #define SPATHNAME_FMT "%s/" TEST_DIR1 "/" TEST_FILE1 84 85 static char dpathname[PATH_MAX]; 86 static char spathname[PATH_MAX]; 87 static int olddirfd, newdirfd = -1, cwd_fd = AT_FDCWD, stdinfd = 0, badfd = 88 -1, deldirfd; 89 90 struct test_struct { 91 int *oldfd; 92 const char *oldfn; 93 int *newfd; 94 const char *newfn; 95 int flags; 96 const char *referencefn1; 97 const char *referencefn2; 98 int expected_errno; 99 } test_desc[] = { 100 /* 1. relative paths */ 101 { 102 &olddirfd, TEST_FILE1, &newdirfd, TEST_FILE1, 0, 103 TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0}, 104 /* 2. abs path at source */ 105 { 106 &olddirfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0}, 107 /* 3. abs path at dst */ 108 { 109 &olddirfd, TEST_FILE1, &newdirfd, dpathname, 0, 110 TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0}, 111 /* 4. relative paths to cwd */ 112 { 113 &cwd_fd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0, 114 TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0}, 115 /* 5. relative paths to cwd */ 116 { 117 &olddirfd, TEST_FILE1, &cwd_fd, TEST_DIR2 "/" TEST_FILE1, 0, 118 TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0}, 119 /* 6. abs path at source */ 120 { 121 &cwd_fd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0}, 122 /* 7. abs path at dst */ 123 { 124 &olddirfd, TEST_FILE1, &cwd_fd, dpathname, 0, 125 TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0}, 126 /* 8. relative paths to invalid */ 127 { 128 &stdinfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0, 129 0, 0, ENOTDIR}, 130 /* 9. relative paths to invalid */ 131 { 132 &olddirfd, TEST_FILE1, &stdinfd, TEST_DIR2 "/" TEST_FILE1, 0, 133 0, 0, ENOTDIR}, 134 /* 10. abs path at source */ 135 { 136 &stdinfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0}, 137 /* 11. abs path at dst */ 138 { 139 &olddirfd, TEST_FILE1, &stdinfd, dpathname, 0, 140 TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0}, 141 /* 12. relative paths to bad */ 142 { 143 &badfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0, 144 0, 0, EBADF}, 145 /* 13. relative paths to bad */ 146 { 147 &olddirfd, TEST_FILE1, &badfd, TEST_DIR2 "/" TEST_FILE1, 0, 148 0, 0, EBADF}, 149 /* 14. abs path at source */ 150 { 151 &badfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0}, 152 /* 15. abs path at dst */ 153 { 154 &olddirfd, TEST_FILE1, &badfd, dpathname, 0, 155 TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0}, 156 /* 16. relative paths to deleted */ 157 { 158 &deldirfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0, 159 0, 0, ENOENT}, 160 /* 17. relative paths to deleted */ 161 { 162 &olddirfd, TEST_FILE1, &deldirfd, TEST_DIR2 "/" TEST_FILE1, 0, 163 0, 0, ENOENT}, 164 /* 18. abs path at source */ 165 { 166 &deldirfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0}, 167 /* 19. abs path at dst */ 168 { 169 &olddirfd, TEST_FILE1, &deldirfd, dpathname, 0, 170 TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0}, 171 /* 20. x-device link */ 172 { 173 &cwd_fd, "/proc/cpuinfo", &newdirfd, TEST_FILE1, 0, 0, 0, EXDEV}, 174 /* 21. directory link */ 175 { 176 &olddirfd, ".", &newdirfd, TEST_FILE1, 0, 0, 0, EPERM}, 177 /* 22. invalid flag */ 178 { 179 &olddirfd, TEST_FILE1, &newdirfd, TEST_FILE1, 1, 0, 0, EINVAL}, 180 /* 23. fifo link */ 181 /* XXX (garrcoop): Removed because it hangs the overall test. Need to 182 * find a legitimate means to exercise this functionality, if in fact 183 * it's a valid testcase -- which it should be. 184 */ 185 /* { &olddirfd, TEST_FIFO, &newdirfd, TEST_FILE1, 0, 186 TEST_DIR1"/"TEST_FIFO, TEST_DIR2"/"TEST_FILE1, 0 } */ 187 }; 188 189 char *TCID = "linkat01"; 190 int TST_TOTAL = sizeof(test_desc) / sizeof(*test_desc); 191 192 static int mylinkat(int olddirfd, const char *oldfilename, int newdirfd, 193 const char *newfilename, int flags) 194 { 195 return ltp_syscall(__NR_linkat, olddirfd, oldfilename, newdirfd, 196 newfilename, flags); 197 } 198 199 int main(int ac, char **av) 200 { 201 int lc; 202 int i; 203 204 if ((tst_kvercmp(2, 6, 16)) < 0) { 205 tst_resm(TWARN, "This test can only run on kernels that are "); 206 tst_resm(TWARN, "2.6.16 and higher"); 207 exit(0); 208 } 209 210 tst_parse_opts(ac, av, NULL, NULL); 211 212 setup(); 213 214 for (lc = 0; TEST_LOOPING(lc); lc++) { 215 216 tst_count = 0; 217 218 for (i = 0; i < TST_TOTAL; i++) { 219 setup_every_copy(); 220 mylinkat_test(&test_desc[i]); 221 } 222 223 } 224 225 cleanup(); 226 tst_exit(); 227 } 228 229 static void setup_every_copy(void) 230 { 231 close(newdirfd); 232 unlink(dpathname); 233 rmdir(TEST_DIR2); 234 235 SAFE_MKDIR(cleanup, TEST_DIR2, 0700); 236 newdirfd = SAFE_OPEN(cleanup, TEST_DIR2, O_DIRECTORY); 237 } 238 239 static void mylinkat_test(struct test_struct *desc) 240 { 241 int fd; 242 243 TEST(mylinkat 244 (*desc->oldfd, desc->oldfn, *desc->newfd, desc->newfn, 245 desc->flags)); 246 247 if (TEST_ERRNO == desc->expected_errno) { 248 if (TEST_RETURN == 0 && desc->referencefn1 != NULL) { 249 int tnum = rand(), vnum = ~tnum; 250 fd = SAFE_OPEN(cleanup, desc->referencefn1, 251 O_RDWR); 252 SAFE_WRITE(cleanup, 1, fd, &tnum, sizeof(tnum)); 253 SAFE_CLOSE(cleanup, fd); 254 255 fd = SAFE_OPEN(cleanup, desc->referencefn2, 256 O_RDONLY); 257 SAFE_READ(cleanup, 1, fd, &vnum, sizeof(vnum)); 258 SAFE_CLOSE(cleanup, fd); 259 260 if (tnum == vnum) 261 tst_resm(TPASS, 262 "linkat is functionality correct"); 263 else { 264 tst_resm(TFAIL, 265 "The link file's content isn't " 266 "as same as the original file's " 267 "although linkat returned 0"); 268 } 269 } else { 270 if (TEST_RETURN == 0) 271 tst_resm(TPASS, 272 "linkat succeeded as expected"); 273 else 274 tst_resm(TPASS | TTERRNO, 275 "linkat failed as expected"); 276 } 277 } else { 278 if (TEST_RETURN == 0) 279 tst_resm(TFAIL, "linkat succeeded unexpectedly"); 280 else 281 tst_resm(TFAIL | TTERRNO, 282 "linkat failed unexpectedly; expected %d - %s", 283 desc->expected_errno, 284 strerror(desc->expected_errno)); 285 } 286 } 287 288 void setup(void) 289 { 290 char *cwd; 291 int fd; 292 293 tst_sig(NOFORK, DEF_HANDLER, cleanup); 294 295 tst_tmpdir(); 296 297 cwd = get_current_dir_name(); 298 if (cwd == NULL) { 299 tst_brkm(TFAIL | TERRNO, cleanup, 300 "Failed to get current working directory"); 301 } 302 303 SAFE_MKDIR(cleanup, TEST_DIR1, 0700); 304 SAFE_MKDIR(cleanup, TEST_DIR3, 0700); 305 olddirfd = SAFE_OPEN(cleanup, TEST_DIR1, O_DIRECTORY); 306 deldirfd = SAFE_OPEN(cleanup, TEST_DIR3, O_DIRECTORY); 307 SAFE_RMDIR(cleanup, TEST_DIR3); 308 fd = SAFE_OPEN(cleanup, TEST_DIR1 "/" TEST_FILE1, O_CREAT | O_EXCL, 0600); 309 SAFE_CLOSE(cleanup, fd); 310 SAFE_MKFIFO(cleanup, TEST_DIR1 "/" TEST_FIFO, 0600); 311 312 snprintf(dpathname, sizeof(dpathname), DPATHNAME_FMT, cwd); 313 snprintf(spathname, sizeof(spathname), SPATHNAME_FMT, cwd); 314 315 free(cwd); 316 } 317 318 static void cleanup(void) 319 { 320 tst_rmdir(); 321 } 322