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
36struct 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
45struct 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
61int _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
73static int afs_entry_point = UNKNOWN_ENTRY_POINT;
74static int afs_syscalls[2];
75static char *afs_ioctlpath;
76static unsigned long afs_ioctlnum;
77
78/* Magic to get AIX syscalls to work */
79#ifdef _AIX
80
81static int (*Pioctl)(char*, int, struct ViceIoctl*, int);
82static int (*Setpag)(void);
83
84#include "dlfcn.h"
85
86/*
87 *
88 */
89
90static int
91try_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
135static int
136map_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
166static int
167try_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
209static int
210do_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
225int
226k_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
281int
282k_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
292int
293k_unlog(void)
294{
295    struct ViceIoctl parms;
296    memset(&parms, 0, sizeof(parms));
297    return k_pioctl(0, VIOCUNLOG, &parms, 0);
298}
299
300int
301k_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
340static jmp_buf catch_SIGSYS;
341
342#ifdef SIGSYS
343
344static RETSIGTYPE
345SIGSYS_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)
359static int
360try_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
385static int
386try_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
405int
406k_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
556done:
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
565int
566k_hasafs_recheck(void)
567{
568    afs_entry_point = UNKNOWN_ENTRY_POINT;
569    return k_hasafs();
570}
571