1 /* 2 * Copyright (c) 1995 - 2000, 2002, 2004, 2005 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "kafs_locl.h" 35 36 struct procdata { 37 unsigned long param4; 38 unsigned long param3; 39 unsigned long param2; 40 unsigned long param1; 41 unsigned long syscall; 42 }; 43 #define VIOC_SYSCALL_PROC _IOW('C', 1, void *) 44 45 struct devdata { 46 unsigned long syscall; 47 unsigned long param1; 48 unsigned long param2; 49 unsigned long param3; 50 unsigned long param4; 51 unsigned long param5; 52 unsigned long param6; 53 unsigned long retval; 54 }; 55 #ifdef _IOWR 56 #define VIOC_SYSCALL_DEV _IOWR('C', 2, struct devdata) 57 #define VIOC_SYSCALL_DEV_OPENAFS _IOWR('C', 1, struct devdata) 58 #endif 59 60 61 int _kafs_debug; /* this should be done in a better way */ 62 63 #define UNKNOWN_ENTRY_POINT (-1) 64 #define NO_ENTRY_POINT 0 65 #define SINGLE_ENTRY_POINT 1 66 #define MULTIPLE_ENTRY_POINT 2 67 #define SINGLE_ENTRY_POINT2 3 68 #define SINGLE_ENTRY_POINT3 4 69 #define LINUX_PROC_POINT 5 70 #define AIX_ENTRY_POINTS 6 71 #define MACOS_DEV_POINT 7 72 73 static int afs_entry_point = UNKNOWN_ENTRY_POINT; 74 static int afs_syscalls[2]; 75 static char *afs_ioctlpath; 76 static unsigned long afs_ioctlnum; 77 78 /* Magic to get AIX syscalls to work */ 79 #ifdef _AIX 80 81 static int (*Pioctl)(char*, int, struct ViceIoctl*, int); 82 static int (*Setpag)(void); 83 84 #include "dlfcn.h" 85 86 /* 87 * 88 */ 89 90 static int 91 try_aix(void) 92 { 93 #ifdef STATIC_AFS_SYSCALLS 94 Pioctl = aix_pioctl; 95 Setpag = aix_setpag; 96 #else 97 void *ptr; 98 char path[MaxPathLen], *p; 99 /* 100 * If we are root or running setuid don't trust AFSLIBPATH! 101 */ 102 if (getuid() != 0 && !issuid() && (p = getenv("AFSLIBPATH")) != NULL) 103 strlcpy(path, p, sizeof(path)); 104 else 105 snprintf(path, sizeof(path), "%s/afslib.so", LIBDIR); 106 107 ptr = dlopen(path, RTLD_NOW); 108 if(ptr == NULL) { 109 if(_kafs_debug) { 110 if(errno == ENOEXEC && (p = dlerror()) != NULL) 111 fprintf(stderr, "dlopen(%s): %s\n", path, p); 112 else if (errno != ENOENT) 113 fprintf(stderr, "dlopen(%s): %s\n", path, strerror(errno)); 114 } 115 return 1; 116 } 117 Setpag = (int (*)(void))dlsym(ptr, "aix_setpag"); 118 Pioctl = (int (*)(char*, int, 119 struct ViceIoctl*, int))dlsym(ptr, "aix_pioctl"); 120 #endif 121 afs_entry_point = AIX_ENTRY_POINTS; 122 return 0; 123 } 124 #endif /* _AIX */ 125 126 /* 127 * This probably only works under Solaris and could get confused if 128 * there's a /etc/name_to_sysnum file. 129 */ 130 131 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 132 133 #define _PATH_ETC_NAME_TO_SYSNUM "/etc/name_to_sysnum" 134 135 static int 136 map_syscall_name_to_number (const char *str, int *res) 137 { 138 FILE *f; 139 char buf[256]; 140 size_t str_len = strlen (str); 141 142 f = fopen (_PATH_ETC_NAME_TO_SYSNUM, "r"); 143 if (f == NULL) 144 return -1; 145 while (fgets (buf, sizeof(buf), f) != NULL) { 146 if (buf[0] == '#') 147 continue; 148 149 if (strncmp (str, buf, str_len) == 0) { 150 char *begptr = buf + str_len; 151 char *endptr; 152 long val = strtol (begptr, &endptr, 0); 153 154 if (val != 0 && endptr != begptr) { 155 fclose (f); 156 *res = val; 157 return 0; 158 } 159 } 160 } 161 fclose (f); 162 return -1; 163 } 164 #endif 165 166 static int 167 try_ioctlpath(const char *path, unsigned long ioctlnum, int entrypoint) 168 { 169 int fd, ret, saved_errno; 170 171 fd = open(path, O_RDWR); 172 if (fd < 0) 173 return 1; 174 switch (entrypoint) { 175 case LINUX_PROC_POINT: { 176 struct procdata data = { 0, 0, 0, 0, AFSCALL_PIOCTL }; 177 data.param2 = (unsigned long)VIOCGETTOK; 178 ret = ioctl(fd, ioctlnum, &data); 179 break; 180 } 181 case MACOS_DEV_POINT: { 182 struct devdata data = { AFSCALL_PIOCTL, 0, 0, 0, 0, 0, 0, 0 }; 183 data.param2 = (unsigned long)VIOCGETTOK; 184 ret = ioctl(fd, ioctlnum, &data); 185 break; 186 } 187 default: 188 abort(); 189 } 190 saved_errno = errno; 191 close(fd); 192 /* 193 * Be quite liberal in what error are ok, the first is the one 194 * that should trigger given that params is NULL. 195 */ 196 if (ret && 197 (saved_errno != EFAULT && 198 saved_errno != EDOM && 199 saved_errno != ENOTCONN)) 200 return 1; 201 afs_ioctlnum = ioctlnum; 202 afs_ioctlpath = strdup(path); 203 if (afs_ioctlpath == NULL) 204 return 1; 205 afs_entry_point = entrypoint; 206 return 0; 207 } 208 209 static int 210 do_ioctl(void *data) 211 { 212 int fd, ret, saved_errno; 213 fd = open(afs_ioctlpath, O_RDWR); 214 if (fd < 0) { 215 errno = EINVAL; 216 return -1; 217 } 218 ret = ioctl(fd, afs_ioctlnum, data); 219 saved_errno = errno; 220 close(fd); 221 errno = saved_errno; 222 return ret; 223 } 224 225 int 226 k_pioctl(char *a_path, 227 int o_opcode, 228 struct ViceIoctl *a_paramsP, 229 int a_followSymlinks) 230 { 231 #ifndef NO_AFS 232 switch(afs_entry_point){ 233 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 234 case SINGLE_ENTRY_POINT: 235 case SINGLE_ENTRY_POINT2: 236 case SINGLE_ENTRY_POINT3: 237 return syscall(afs_syscalls[0], AFSCALL_PIOCTL, 238 a_path, o_opcode, a_paramsP, a_followSymlinks); 239 #endif 240 #if defined(AFS_PIOCTL) 241 case MULTIPLE_ENTRY_POINT: 242 return syscall(afs_syscalls[0], 243 a_path, o_opcode, a_paramsP, a_followSymlinks); 244 #endif 245 case LINUX_PROC_POINT: { 246 struct procdata data = { 0, 0, 0, 0, AFSCALL_PIOCTL }; 247 data.param1 = (unsigned long)a_path; 248 data.param2 = (unsigned long)o_opcode; 249 data.param3 = (unsigned long)a_paramsP; 250 data.param4 = (unsigned long)a_followSymlinks; 251 return do_ioctl(&data); 252 } 253 case MACOS_DEV_POINT: { 254 struct devdata data = { AFSCALL_PIOCTL, 0, 0, 0, 0, 0, 0, 0 }; 255 int ret; 256 257 data.param1 = (unsigned long)a_path; 258 data.param2 = (unsigned long)o_opcode; 259 data.param3 = (unsigned long)a_paramsP; 260 data.param4 = (unsigned long)a_followSymlinks; 261 262 ret = do_ioctl(&data); 263 if (ret) 264 return ret; 265 266 return data.retval; 267 } 268 #ifdef _AIX 269 case AIX_ENTRY_POINTS: 270 return Pioctl(a_path, o_opcode, a_paramsP, a_followSymlinks); 271 #endif 272 } 273 errno = ENOSYS; 274 #ifdef SIGSYS 275 kill(getpid(), SIGSYS); /* You lose! */ 276 #endif 277 #endif /* NO_AFS */ 278 return -1; 279 } 280 281 int 282 k_afs_cell_of_file(const char *path, char *cell, int len) 283 { 284 struct ViceIoctl parms; 285 parms.in = NULL; 286 parms.in_size = 0; 287 parms.out = cell; 288 parms.out_size = len; 289 return k_pioctl(rk_UNCONST(path), VIOC_FILE_CELL_NAME, &parms, 1); 290 } 291 292 int 293 k_unlog(void) 294 { 295 struct ViceIoctl parms; 296 memset(&parms, 0, sizeof(parms)); 297 return k_pioctl(0, VIOCUNLOG, &parms, 0); 298 } 299 300 int 301 k_setpag(void) 302 { 303 #ifndef NO_AFS 304 switch(afs_entry_point){ 305 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 306 case SINGLE_ENTRY_POINT: 307 case SINGLE_ENTRY_POINT2: 308 case SINGLE_ENTRY_POINT3: 309 return syscall(afs_syscalls[0], AFSCALL_SETPAG); 310 #endif 311 #if defined(AFS_PIOCTL) 312 case MULTIPLE_ENTRY_POINT: 313 return syscall(afs_syscalls[1]); 314 #endif 315 case LINUX_PROC_POINT: { 316 struct procdata data = { 0, 0, 0, 0, AFSCALL_SETPAG }; 317 return do_ioctl(&data); 318 } 319 case MACOS_DEV_POINT: { 320 struct devdata data = { AFSCALL_SETPAG, 0, 0, 0, 0, 0, 0, 0 }; 321 int ret = do_ioctl(&data); 322 if (ret) 323 return ret; 324 return data.retval; 325 } 326 #ifdef _AIX 327 case AIX_ENTRY_POINTS: 328 return Setpag(); 329 #endif 330 } 331 332 errno = ENOSYS; 333 #ifdef SIGSYS 334 kill(getpid(), SIGSYS); /* You lose! */ 335 #endif 336 #endif /* NO_AFS */ 337 return -1; 338 } 339 340 static jmp_buf catch_SIGSYS; 341 342 #ifdef SIGSYS 343 344 static RETSIGTYPE 345 SIGSYS_handler(int sig) 346 { 347 errno = 0; 348 signal(SIGSYS, SIGSYS_handler); /* Need to reinstall handler on SYSV */ 349 longjmp(catch_SIGSYS, 1); 350 } 351 352 #endif 353 354 /* 355 * Try to see if `syscall' is a pioctl. Return 0 iff succesful. 356 */ 357 358 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 359 static int 360 try_one (int syscall_num) 361 { 362 struct ViceIoctl parms; 363 memset(&parms, 0, sizeof(parms)); 364 365 if (setjmp(catch_SIGSYS) == 0) { 366 syscall(syscall_num, AFSCALL_PIOCTL, 367 0, VIOCSETTOK, &parms, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 368 if (errno == EINVAL) { 369 afs_entry_point = SINGLE_ENTRY_POINT; 370 afs_syscalls[0] = syscall_num; 371 return 0; 372 } 373 } 374 return 1; 375 } 376 #endif 377 378 /* 379 * Try to see if `syscall_pioctl' is a pioctl syscall. Return 0 iff 380 * succesful. 381 * 382 */ 383 384 #ifdef AFS_PIOCTL 385 static int 386 try_two (int syscall_pioctl, int syscall_setpag) 387 { 388 struct ViceIoctl parms; 389 memset(&parms, 0, sizeof(parms)); 390 391 if (setjmp(catch_SIGSYS) == 0) { 392 syscall(syscall_pioctl, 393 0, VIOCSETTOK, &parms, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 394 if (errno == EINVAL) { 395 afs_entry_point = MULTIPLE_ENTRY_POINT; 396 afs_syscalls[0] = syscall_pioctl; 397 afs_syscalls[1] = syscall_setpag; 398 return 0; 399 } 400 } 401 return 1; 402 } 403 #endif 404 405 int 406 k_hasafs(void) 407 { 408 #if !defined(NO_AFS) && defined(SIGSYS) 409 RETSIGTYPE (*saved_func)(int); 410 #endif 411 int saved_errno, ret; 412 char *env = NULL; 413 414 if (!issuid()) 415 env = getenv ("AFS_SYSCALL"); 416 417 /* 418 * Already checked presence of AFS syscalls? 419 */ 420 if (afs_entry_point != UNKNOWN_ENTRY_POINT) 421 return afs_entry_point != NO_ENTRY_POINT; 422 423 /* 424 * Probe kernel for AFS specific syscalls, 425 * they (currently) come in two flavors. 426 * If the syscall is absent we recive a SIGSYS. 427 */ 428 afs_entry_point = NO_ENTRY_POINT; 429 430 saved_errno = errno; 431 #ifndef NO_AFS 432 #ifdef SIGSYS 433 saved_func = signal(SIGSYS, SIGSYS_handler); 434 #endif 435 if (env && strstr(env, "..") == NULL) { 436 437 if (strncmp("/proc/", env, 6) == 0) { 438 if (try_ioctlpath(env, VIOC_SYSCALL_PROC, LINUX_PROC_POINT) == 0) 439 goto done; 440 } 441 if (strncmp("/dev/", env, 5) == 0) { 442 #ifdef VIOC_SYSCALL_DEV 443 if (try_ioctlpath(env, VIOC_SYSCALL_DEV, MACOS_DEV_POINT) == 0) 444 goto done; 445 #endif 446 #ifdef VIOC_SYSCALL_DEV_OPENAFS 447 if (try_ioctlpath(env,VIOC_SYSCALL_DEV_OPENAFS,MACOS_DEV_POINT) ==0) 448 goto done; 449 #endif 450 } 451 } 452 453 ret = try_ioctlpath("/proc/fs/openafs/afs_ioctl", 454 VIOC_SYSCALL_PROC, LINUX_PROC_POINT); 455 if (ret == 0) 456 goto done; 457 ret = try_ioctlpath("/proc/fs/nnpfs/afs_ioctl", 458 VIOC_SYSCALL_PROC, LINUX_PROC_POINT); 459 if (ret == 0) 460 goto done; 461 462 #ifdef VIOC_SYSCALL_DEV_OPENAFS 463 ret = try_ioctlpath("/dev/openafs_ioctl", 464 VIOC_SYSCALL_DEV_OPENAFS, MACOS_DEV_POINT); 465 if (ret == 0) 466 goto done; 467 #endif 468 #ifdef VIOC_SYSCALL_DEV 469 ret = try_ioctlpath("/dev/nnpfs_ioctl", VIOC_SYSCALL_DEV, MACOS_DEV_POINT); 470 if (ret == 0) 471 goto done; 472 #endif 473 474 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 475 { 476 int tmp; 477 478 if (env != NULL) { 479 if (sscanf (env, "%d", &tmp) == 1) { 480 if (try_one (tmp) == 0) 481 goto done; 482 } else { 483 char *end = NULL; 484 char *p; 485 char *s = strdup (env); 486 487 if (s != NULL) { 488 for (p = strtok_r (s, ",", &end); 489 p != NULL; 490 p = strtok_r (NULL, ",", &end)) { 491 if (map_syscall_name_to_number (p, &tmp) == 0) 492 if (try_one (tmp) == 0) { 493 free (s); 494 goto done; 495 } 496 } 497 free (s); 498 } 499 } 500 } 501 } 502 #endif /* AFS_SYSCALL || AFS_SYSCALL2 || AFS_SYSCALL3 */ 503 504 #ifdef AFS_SYSCALL 505 if (try_one (AFS_SYSCALL) == 0) 506 goto done; 507 #endif /* AFS_SYSCALL */ 508 509 #ifdef AFS_PIOCTL 510 { 511 int tmp[2]; 512 513 if (env != NULL && sscanf (env, "%d%d", &tmp[0], &tmp[1]) == 2) 514 if (try_two (tmp[0], tmp[1]) == 2) 515 goto done; 516 } 517 #endif /* AFS_PIOCTL */ 518 519 #ifdef AFS_PIOCTL 520 if (try_two (AFS_PIOCTL, AFS_SETPAG) == 0) 521 goto done; 522 #endif /* AFS_PIOCTL */ 523 524 #ifdef AFS_SYSCALL2 525 if (try_one (AFS_SYSCALL2) == 0) 526 goto done; 527 #endif /* AFS_SYSCALL2 */ 528 529 #ifdef AFS_SYSCALL3 530 if (try_one (AFS_SYSCALL3) == 0) 531 goto done; 532 #endif /* AFS_SYSCALL3 */ 533 534 #ifdef _AIX 535 #if 0 536 if (env != NULL) { 537 char *pos = NULL; 538 char *pioctl_name; 539 char *setpag_name; 540 541 pioctl_name = strtok_r (env, ", \t", &pos); 542 if (pioctl_name != NULL) { 543 setpag_name = strtok_r (NULL, ", \t", &pos); 544 if (setpag_name != NULL) 545 if (try_aix (pioctl_name, setpag_name) == 0) 546 goto done; 547 } 548 } 549 #endif 550 551 if(try_aix() == 0) 552 goto done; 553 #endif 554 555 556 done: 557 #ifdef SIGSYS 558 signal(SIGSYS, saved_func); 559 #endif 560 #endif /* NO_AFS */ 561 errno = saved_errno; 562 return afs_entry_point != NO_ENTRY_POINT; 563 } 564 565 int 566 k_hasafs_recheck(void) 567 { 568 afs_entry_point = UNKNOWN_ENTRY_POINT; 569 return k_hasafs(); 570 } 571