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