afssys.c revision 1.1.1.1
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
38struct 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
47struct 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
63int _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
75static int afs_entry_point = UNKNOWN_ENTRY_POINT;
76static int afs_syscalls[2];
77static char *afs_ioctlpath;
78static unsigned long afs_ioctlnum;
79
80/* Magic to get AIX syscalls to work */
81#ifdef _AIX
82
83static int (*Pioctl)(char*, int, struct ViceIoctl*, int);
84static int (*Setpag)(void);
85
86#include "dlfcn.h"
87
88/*
89 *
90 */
91
92static int
93try_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
137static int
138map_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
168static int
169try_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
211static int
212do_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
227int
228k_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
283int
284k_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
294int
295k_unlog(void)
296{
297    struct ViceIoctl parms;
298    memset(&parms, 0, sizeof(parms));
299    return k_pioctl(0, VIOCUNLOG, &parms, 0);
300}
301
302int
303k_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
342static jmp_buf catch_SIGSYS;
343
344#ifdef SIGSYS
345
346static RETSIGTYPE
347SIGSYS_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)
361static int
362try_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
387static int
388try_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
407int
408k_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
558done:
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
567int
568k_hasafs_recheck(void)
569{
570    afs_entry_point = UNKNOWN_ENTRY_POINT;
571    return k_hasafs();
572}
573