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