1 /* 2 * Copyright (c) 1995 - 2000, 2002, 2004 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 RCSID("$Id: afssys.c,v 1.69.2.2 2004/06/22 14:29:48 lha Exp $"); 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 _IOW('C', 1, void *) 46 47 48 int _kafs_debug; /* this should be done in a better way */ 49 50 #define NO_ENTRY_POINT 0 51 #define SINGLE_ENTRY_POINT 1 52 #define MULTIPLE_ENTRY_POINT 2 53 #define SINGLE_ENTRY_POINT2 3 54 #define SINGLE_ENTRY_POINT3 4 55 #define LINUX_PROC_POINT 5 56 #define AIX_ENTRY_POINTS 6 57 #define UNKNOWN_ENTRY_POINT 7 58 static int afs_entry_point = UNKNOWN_ENTRY_POINT; 59 static int afs_syscalls[2]; 60 static char *afs_procpath; 61 62 /* Magic to get AIX syscalls to work */ 63 #ifdef _AIX 64 65 static int (*Pioctl)(char*, int, struct ViceIoctl*, int); 66 static int (*Setpag)(void); 67 68 #include "dlfcn.h" 69 70 /* 71 * 72 */ 73 74 static int 75 try_aix(void) 76 { 77 #ifdef STATIC_AFS_SYSCALLS 78 Pioctl = aix_pioctl; 79 Setpag = aix_setpag; 80 #else 81 void *ptr; 82 char path[MaxPathLen], *p; 83 /* 84 * If we are root or running setuid don't trust AFSLIBPATH! 85 */ 86 if (getuid() != 0 && !issuid() && (p = getenv("AFSLIBPATH")) != NULL) 87 strlcpy(path, p, sizeof(path)); 88 else 89 snprintf(path, sizeof(path), "%s/afslib.so", LIBDIR); 90 91 ptr = dlopen(path, RTLD_NOW); 92 if(ptr == NULL) { 93 if(_kafs_debug) { 94 if(errno == ENOEXEC && (p = dlerror()) != NULL) 95 fprintf(stderr, "dlopen(%s): %s\n", path, p); 96 else if (errno != ENOENT) 97 fprintf(stderr, "dlopen(%s): %s\n", path, strerror(errno)); 98 } 99 return 1; 100 } 101 Setpag = (int (*)(void))dlsym(ptr, "aix_setpag"); 102 Pioctl = (int (*)(char*, int, 103 struct ViceIoctl*, int))dlsym(ptr, "aix_pioctl"); 104 #endif 105 afs_entry_point = AIX_ENTRY_POINTS; 106 return 0; 107 } 108 #endif /* _AIX */ 109 110 /* 111 * This probably only works under Solaris and could get confused if 112 * there's a /etc/name_to_sysnum file. 113 */ 114 115 #define _PATH_ETC_NAME_TO_SYSNUM "/etc/name_to_sysnum" 116 117 static int 118 map_syscall_name_to_number (const char *str, int *res) 119 { 120 FILE *f; 121 char buf[256]; 122 size_t str_len = strlen (str); 123 124 f = fopen (_PATH_ETC_NAME_TO_SYSNUM, "r"); 125 if (f == NULL) 126 return -1; 127 while (fgets (buf, sizeof(buf), f) != NULL) { 128 if (buf[0] == '#') 129 continue; 130 131 if (strncmp (str, buf, str_len) == 0) { 132 char *begptr = buf + str_len; 133 char *endptr; 134 long val = strtol (begptr, &endptr, 0); 135 136 if (val != 0 && endptr != begptr) { 137 fclose (f); 138 *res = val; 139 return 0; 140 } 141 } 142 } 143 fclose (f); 144 return -1; 145 } 146 147 static int 148 try_proc(const char *path) 149 { 150 int fd; 151 fd = open(path, O_RDWR); 152 if (fd < 0) 153 return 1; 154 close(fd); 155 afs_procpath = strdup(path); 156 if (afs_procpath == NULL) 157 return 1; 158 afs_entry_point = LINUX_PROC_POINT; 159 return 0; 160 } 161 162 static int 163 do_proc(struct procdata *data) 164 { 165 int fd, ret, saved_errno; 166 fd = open(afs_procpath, O_RDWR); 167 if (fd < 0) { 168 errno = EINVAL; 169 return -1; 170 } 171 ret = ioctl(fd, VIOC_SYSCALL, data); 172 saved_errno = errno; 173 close(fd); 174 errno = saved_errno; 175 return ret; 176 } 177 178 int 179 k_pioctl(char *a_path, 180 int o_opcode, 181 struct ViceIoctl *a_paramsP, 182 int a_followSymlinks) 183 { 184 #ifndef NO_AFS 185 switch(afs_entry_point){ 186 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 187 case SINGLE_ENTRY_POINT: 188 case SINGLE_ENTRY_POINT2: 189 case SINGLE_ENTRY_POINT3: 190 return syscall(afs_syscalls[0], AFSCALL_PIOCTL, 191 a_path, o_opcode, a_paramsP, a_followSymlinks); 192 #endif 193 #if defined(AFS_PIOCTL) 194 case MULTIPLE_ENTRY_POINT: 195 return syscall(afs_syscalls[0], 196 a_path, o_opcode, a_paramsP, a_followSymlinks); 197 #endif 198 case LINUX_PROC_POINT: { 199 struct procdata data = { 0, 0, 0, 0, AFSCALL_PIOCTL }; 200 data.param1 = (unsigned long)a_path; 201 data.param2 = (unsigned long)o_opcode; 202 data.param3 = (unsigned long)a_paramsP; 203 data.param4 = (unsigned long)a_followSymlinks; 204 return do_proc(&data); 205 } 206 #ifdef _AIX 207 case AIX_ENTRY_POINTS: 208 return Pioctl(a_path, o_opcode, a_paramsP, a_followSymlinks); 209 #endif 210 } 211 errno = ENOSYS; 212 #ifdef SIGSYS 213 kill(getpid(), SIGSYS); /* You lose! */ 214 #endif 215 #endif /* NO_AFS */ 216 return -1; 217 } 218 219 int 220 k_afs_cell_of_file(const char *path, char *cell, int len) 221 { 222 struct ViceIoctl parms; 223 parms.in = NULL; 224 parms.in_size = 0; 225 parms.out = cell; 226 parms.out_size = len; 227 return k_pioctl((char*)path, VIOC_FILE_CELL_NAME, &parms, 1); 228 } 229 230 int 231 k_unlog(void) 232 { 233 struct ViceIoctl parms; 234 memset(&parms, 0, sizeof(parms)); 235 return k_pioctl(0, VIOCUNLOG, &parms, 0); 236 } 237 238 int 239 k_setpag(void) 240 { 241 #ifndef NO_AFS 242 switch(afs_entry_point){ 243 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 244 case SINGLE_ENTRY_POINT: 245 case SINGLE_ENTRY_POINT2: 246 case SINGLE_ENTRY_POINT3: 247 return syscall(afs_syscalls[0], AFSCALL_SETPAG); 248 #endif 249 #if defined(AFS_PIOCTL) 250 case MULTIPLE_ENTRY_POINT: 251 return syscall(afs_syscalls[1]); 252 #endif 253 case LINUX_PROC_POINT: { 254 struct procdata data = { 0, 0, 0, 0, AFSCALL_SETPAG }; 255 return do_proc(&data); 256 } 257 #ifdef _AIX 258 case AIX_ENTRY_POINTS: 259 return Setpag(); 260 #endif 261 } 262 263 errno = ENOSYS; 264 #ifdef SIGSYS 265 kill(getpid(), SIGSYS); /* You lose! */ 266 #endif 267 #endif /* NO_AFS */ 268 return -1; 269 } 270 271 static jmp_buf catch_SIGSYS; 272 273 #ifdef SIGSYS 274 275 static RETSIGTYPE 276 SIGSYS_handler(int sig) 277 { 278 errno = 0; 279 signal(SIGSYS, SIGSYS_handler); /* Need to reinstall handler on SYSV */ 280 longjmp(catch_SIGSYS, 1); 281 } 282 283 #endif 284 285 /* 286 * Try to see if `syscall' is a pioctl. Return 0 iff succesful. 287 */ 288 289 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 290 static int 291 try_one (int syscall_num) 292 { 293 struct ViceIoctl parms; 294 memset(&parms, 0, sizeof(parms)); 295 296 if (setjmp(catch_SIGSYS) == 0) { 297 syscall(syscall_num, AFSCALL_PIOCTL, 298 0, VIOCSETTOK, &parms, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 299 if (errno == EINVAL) { 300 afs_entry_point = SINGLE_ENTRY_POINT; 301 afs_syscalls[0] = syscall_num; 302 return 0; 303 } 304 } 305 return 1; 306 } 307 #endif 308 309 /* 310 * Try to see if `syscall_pioctl' is a pioctl syscall. Return 0 iff 311 * succesful. 312 * 313 */ 314 315 #ifdef AFS_PIOCTL 316 static int 317 try_two (int syscall_pioctl, int syscall_setpag) 318 { 319 struct ViceIoctl parms; 320 memset(&parms, 0, sizeof(parms)); 321 322 if (setjmp(catch_SIGSYS) == 0) { 323 syscall(syscall_pioctl, 324 0, VIOCSETTOK, &parms, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 325 if (errno == EINVAL) { 326 afs_entry_point = MULTIPLE_ENTRY_POINT; 327 afs_syscalls[0] = syscall_pioctl; 328 afs_syscalls[1] = syscall_setpag; 329 return 0; 330 } 331 } 332 return 1; 333 } 334 #endif 335 336 int 337 k_hasafs(void) 338 { 339 #if !defined(NO_AFS) && defined(SIGSYS) 340 RETSIGTYPE (*saved_func)(int); 341 #endif 342 int saved_errno; 343 char *env = getenv ("AFS_SYSCALL"); 344 345 /* 346 * Already checked presence of AFS syscalls? 347 */ 348 if (afs_entry_point != UNKNOWN_ENTRY_POINT) 349 return afs_entry_point != NO_ENTRY_POINT; 350 351 /* 352 * Probe kernel for AFS specific syscalls, 353 * they (currently) come in two flavors. 354 * If the syscall is absent we recive a SIGSYS. 355 */ 356 afs_entry_point = NO_ENTRY_POINT; 357 358 saved_errno = errno; 359 #ifndef NO_AFS 360 #ifdef SIGSYS 361 saved_func = signal(SIGSYS, SIGSYS_handler); 362 #endif 363 364 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3) 365 { 366 int tmp; 367 368 if (env != NULL) { 369 if (sscanf (env, "%d", &tmp) == 1) { 370 if (try_one (tmp) == 0) 371 goto done; 372 } else { 373 char *end = NULL; 374 char *p; 375 char *s = strdup (env); 376 377 if (s != NULL) { 378 for (p = strtok_r (s, ",", &end); 379 p != NULL; 380 p = strtok_r (NULL, ",", &end)) { 381 if (map_syscall_name_to_number (p, &tmp) == 0) 382 if (try_one (tmp) == 0) { 383 free (s); 384 goto done; 385 } 386 } 387 free (s); 388 } 389 } 390 } 391 } 392 #endif /* AFS_SYSCALL || AFS_SYSCALL2 || AFS_SYSCALL3 */ 393 394 #ifdef AFS_SYSCALL 395 if (try_one (AFS_SYSCALL) == 0) 396 goto done; 397 #endif /* AFS_SYSCALL */ 398 399 #ifdef AFS_PIOCTL 400 { 401 int tmp[2]; 402 403 if (env != NULL && sscanf (env, "%d%d", &tmp[0], &tmp[1]) == 2) 404 if (try_two (tmp[0], tmp[1]) == 2) 405 goto done; 406 } 407 #endif /* AFS_PIOCTL */ 408 409 #ifdef AFS_PIOCTL 410 if (try_two (AFS_PIOCTL, AFS_SETPAG) == 0) 411 goto done; 412 #endif /* AFS_PIOCTL */ 413 414 #ifdef AFS_SYSCALL2 415 if (try_one (AFS_SYSCALL2) == 0) 416 goto done; 417 #endif /* AFS_SYSCALL2 */ 418 419 #ifdef AFS_SYSCALL3 420 if (try_one (AFS_SYSCALL3) == 0) 421 goto done; 422 #endif /* AFS_SYSCALL3 */ 423 424 #ifdef _AIX 425 #if 0 426 if (env != NULL) { 427 char *pos = NULL; 428 char *pioctl_name; 429 char *setpag_name; 430 431 pioctl_name = strtok_r (env, ", \t", &pos); 432 if (pioctl_name != NULL) { 433 setpag_name = strtok_r (NULL, ", \t", &pos); 434 if (setpag_name != NULL) 435 if (try_aix (pioctl_name, setpag_name) == 0) 436 goto done; 437 } 438 } 439 #endif 440 441 if(try_aix() == 0) 442 goto done; 443 #endif 444 445 if (try_proc("/proc/fs/openafs/afs_ioctl") == 0) 446 goto done; 447 if (try_proc("/proc/fs/nnpfs/afs_ioctl") == 0) 448 goto done; 449 if (env && try_proc(env) == 0) 450 goto done; 451 452 done: 453 #ifdef SIGSYS 454 signal(SIGSYS, saved_func); 455 #endif 456 #endif /* NO_AFS */ 457 errno = saved_errno; 458 return afs_entry_point != NO_ENTRY_POINT; 459 } 460