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