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
try_aix(void)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
map_syscall_name_to_number(const char * str,int * res)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
try_ioctlpath(const char * path,unsigned long ioctlnum,int entrypoint)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
do_ioctl(void * data)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
k_pioctl(char * a_path,int o_opcode,struct ViceIoctl * a_paramsP,int a_followSymlinks)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
k_afs_cell_of_file(const char * path,char * cell,int len)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
k_unlog(void)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
k_setpag(void)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
SIGSYS_handler(int sig)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
try_one(int syscall_num)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
try_two(int syscall_pioctl,int syscall_setpag)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
k_hasafs(void)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
k_hasafs_recheck(void)627 k_hasafs_recheck(void)
628 {
629 afs_entry_point = UNKNOWN_ENTRY_POINT;
630 return k_hasafs();
631 }
632