• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/netatalk-3.0.5/libatalk/util/
1/*
2  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
3
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  GNU General Public License for more details.
13*/
14
15/*!
16 * @file
17 * Netatalk utility functions
18 */
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif /* HAVE_CONFIG_H */
23
24#include <unistd.h>
25#include <stdint.h>
26#include <errno.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <fcntl.h>
32#include <dirent.h>
33#include <sys/time.h>
34#include <time.h>
35#include <sys/wait.h>
36#include <libgen.h>
37
38#include <atalk/adouble.h>
39#include <atalk/ea.h>
40#include <atalk/afp.h>
41#include <atalk/logger.h>
42#include <atalk/vfs.h>
43#include <atalk/util.h>
44#include <atalk/unix.h>
45#include <atalk/compat.h>
46#include <atalk/errchk.h>
47
48/* close all FDs >= a specified value */
49static void closeall(int fd)
50{
51    int fdlimit = sysconf(_SC_OPEN_MAX);
52
53    while (fd < fdlimit)
54        close(fd++);
55}
56
57/*!
58 * Run command in a child and wait for it to finish
59 */
60int run_cmd(const char *cmd, char **cmd_argv)
61{
62    EC_INIT;
63    pid_t pid, wpid;
64    sigset_t sigs, oldsigs;
65	int status = 0;
66
67    sigfillset(&sigs);
68    pthread_sigmask(SIG_SETMASK, &sigs, &oldsigs);
69
70    if ((pid = fork()) < 0) {
71        LOG(log_error, logtype_default, "run_cmd: fork: %s", strerror(errno));
72        return -1;
73    }
74
75    if (pid == 0) {
76        /* child */
77        closeall(3);
78        execvp("mv", cmd_argv);
79    }
80
81    /* parent */
82	while ((wpid = waitpid(pid, &status, 0)) < 0) {
83	    if (errno == EINTR)
84            continue;
85	    break;
86	}
87	if (wpid != pid) {
88	    LOG(log_error, logtype_default, "waitpid(%d): %s", (int)pid, strerror(errno));
89        EC_FAIL;
90	}
91
92    if (WIFEXITED(status))
93        status = WEXITSTATUS(status);
94    else if (WIFSIGNALED(status))
95        status = WTERMSIG(status);
96
97    LOG(log_note, logtype_default, "run_cmd(\"%s\"): status: %d", cmd, status);
98
99EC_CLEANUP:
100    if (status != 0)
101        ret = status;
102    pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
103    EC_EXIT;
104}
105
106/*!
107 * Daemonize
108 *
109 * Fork, exit parent, setsid(), optionally chdir("/"), optionally close all fds
110 *
111 * returns -1 on failure, but you can't do much except exit in that case
112 * since we may already have forked
113 */
114int daemonize(int nochdir, int noclose)
115{
116    switch (fork()) {
117    case 0:
118        break;
119    case -1:
120        return -1;
121    default:
122        _exit(0);
123    }
124
125    if (setsid() < 0)
126        return -1;
127
128    switch (fork()) {
129    case 0:
130        break;
131    case -1:
132        return -1;
133    default:
134        _exit(0);
135    }
136
137    if (!nochdir)
138        chdir("/");
139
140    if (!noclose) {
141        closeall(0);
142        open("/dev/null",O_RDWR);
143        dup(0);
144        dup(0);
145    }
146
147    return 0;
148}
149
150static uid_t saved_uid = -1;
151
152/*
153 * seteuid(0) and back, if either fails and panic != 0 we PANIC
154 */
155void become_root(void)
156{
157    if (getuid() == 0) {
158        saved_uid = geteuid();
159        if (seteuid(0) != 0)
160            AFP_PANIC("Can't seteuid(0)");
161    }
162}
163
164void unbecome_root(void)
165{
166    if (getuid() == 0) {
167        if (saved_uid == -1 || seteuid(saved_uid) < 0)
168            AFP_PANIC("Can't seteuid back");
169        saved_uid = -1;
170    }
171}
172
173/*!
174 * @brief get cwd in static buffer
175 *
176 * @returns pointer to path or pointer to error messages on error
177 */
178const char *getcwdpath(void)
179{
180    static char cwd[MAXPATHLEN + 1];
181    char *p;
182
183    if ((p = getcwd(cwd, MAXPATHLEN)) != NULL)
184        return p;
185    else
186        return strerror(errno);
187}
188
189/*!
190 * @brief Request absolute path
191 *
192 * @returns Absolute filesystem path to object
193 */
194const char *fullpathname(const char *name)
195{
196    static char wd[MAXPATHLEN + 1];
197
198    if (name[0] == '/')
199        return name;
200
201    if (getcwd(wd , MAXPATHLEN)) {
202        strlcat(wd, "/", MAXPATHLEN);
203        strlcat(wd, name, MAXPATHLEN);
204    } else {
205        strlcpy(wd, name, MAXPATHLEN);
206    }
207
208    return wd;
209}
210
211/*!
212 * Takes a buffer with a path, strips slashs, returns basename
213 *
214 * @param p (rw) path
215 *        path may be
216 *          "[/][dir/[...]]file"
217 *        or
218 *          "[/][dir/[...]]dir/[/]"
219 *        Result is "file" or "dir"
220 *
221 * @returns pointer to basename in path buffer, buffer is possibly modified
222 */
223char *stripped_slashes_basename(char *p)
224{
225    int i = strlen(p) - 1;
226    while (i > 0 && p[i] == '/')
227        p[i--] = 0;
228    return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
229}
230
231/*********************************************************************************
232 * chdir(), chmod(), chown(), stat() wrappers taking an additional option.
233 * Currently the only used options are O_NOFOLLOW, used to switch between symlink
234 * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be
235 * called which does special ACL handling depending on the filesytem
236 *********************************************************************************/
237
238int ostat(const char *path, struct stat *buf, int options)
239{
240    if (options & O_NOFOLLOW)
241        return lstat(path, buf);
242    else
243        return stat(path, buf);
244}
245
246int ochown(const char *path, uid_t owner, gid_t group, int options)
247{
248    if (options & O_NOFOLLOW)
249        return lchown(path, owner, group);
250    else
251        return chown(path, owner, group);
252}
253
254/*!
255 * chmod() wrapper for symlink and ACL handling
256 *
257 * @param path       (r) path
258 * @param mode       (r) requested mode
259 * @param sb         (r) stat() of path or NULL
260 * @param option     (r) O_NOFOLLOW | O_NETATALK_ACL
261 *
262 * Options description:
263 * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0
264 * O_NETATALK_ACL: call chmod_acl() instead of chmod()
265 */
266int ochmod(char *path, mode_t mode, const struct stat *st, int options)
267{
268    struct stat sb;
269
270    if (!st) {
271        if (lstat(path, &sb) != 0)
272            return -1;
273        st = &sb;
274    }
275
276    if (options & O_NOFOLLOW)
277        if (S_ISLNK(st->st_mode))
278            return 0;
279
280    if (options & O_NETATALK_ACL) {
281        return chmod_acl(path, mode);
282    } else {
283        return chmod(path, mode);
284    }
285}
286
287/*
288 * @brief ostat/fsstatat multiplexer
289 *
290 * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
291 *
292 * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
293 * @param path    (r) pathname
294 * @param st      (rw) pointer to struct stat
295 */
296int ostatat(int dirfd, const char *path, struct stat *st, int options)
297{
298#ifdef HAVE_ATFUNCS
299    if (dirfd == -1)
300        dirfd = AT_FDCWD;
301    return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
302#else
303    return ostat(path, st, options);
304#endif
305
306    /* DEADC0DE */
307    return -1;
308}
309
310/*!
311 * @brief symlink safe chdir replacement
312 *
313 * Only chdirs to dir if it doesn't contain symlinks or if symlink checking
314 * is disabled
315 *
316 * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
317 */
318int ochdir(const char *dir, int options)
319{
320    char buf[MAXPATHLEN+1];
321    char cwd[MAXPATHLEN+1];
322    char *test;
323    int  i;
324
325    if (!(options & O_NOFOLLOW))
326        return chdir(dir);
327
328    /*
329     dir is a canonical path (without "../" "./" "//" )
330     but may end with a /
331    */
332    *cwd = 0;
333    if (*dir != '/') {
334        if (getcwd(cwd, MAXPATHLEN) == NULL)
335            return -1;
336    }
337    if (chdir(dir) != 0)
338        return -1;
339
340    /*
341     * Cases:
342     * chdir request   | realpath result | ret
343     * (after getwcwd) |                 |
344     * =======================================
345     * /a/b/.          | /a/b            | 0
346     * /a/b/.          | /c              | 1
347     * /a/b/.          | /c/d/e/f        | 1
348     */
349    if (getcwd(buf, MAXPATHLEN) == NULL)
350        return 1;
351
352    i = 0;
353    if (*cwd) {
354        /* relative path requested,
355         * Same directory?
356        */
357        for (; cwd[i]; i++) {
358            if (buf[i] != cwd[i])
359                return 1;
360        }
361        if (buf[i]) {
362            if (buf[i] != '/')
363                return 1;
364            i++;
365        }
366    }
367
368    test = &buf[i];
369    for (i = 0; test[i]; i++) {
370        if (test[i] != dir[i]) {
371            return 1;
372        }
373    }
374    /* trailing '/' ? */
375    if (!dir[i])
376        return 0;
377
378    if (dir[i] != '/')
379        return 1;
380
381    i++;
382    if (dir[i])
383        return 1;
384
385    return 0;
386}
387
388/*!
389 * Store n random bytes an buf
390 */
391void randombytes(void *buf, int n)
392{
393    char *p = (char *)buf;
394    int fd, i;
395    struct timeval tv;
396
397    if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
398        /* generate from /dev/urandom */
399        if (read(fd, buf, n) != n) {
400            close(fd);
401            fd = -1;
402        } else {
403            close(fd);
404            /* fd now != -1, so srandom wont be called below */
405        }
406    }
407
408    if (fd == -1) {
409        gettimeofday(&tv, NULL);
410        srandom((unsigned int)tv.tv_usec);
411        for (i=0 ; i < n ; i++)
412            p[i] = random() & 0xFF;
413    }
414
415    return;
416}
417
418int gmem(gid_t gid, int ngroups, gid_t *groups)
419{
420    int		i;
421
422    for ( i = 0; i < ngroups; i++ ) {
423        if ( groups[ i ] == gid ) {
424            return( 1 );
425        }
426    }
427    return( 0 );
428}
429
430/*
431 * realpath() replacement that always allocates storage for returned path
432 */
433char *realpath_safe(const char *path)
434{
435    char *resolved_path;
436
437#ifdef REALPATH_TAKES_NULL
438    if ((resolved_path = realpath(path, NULL)) == NULL) {
439        LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
440        return NULL;
441    }
442    return resolved_path;
443#else
444    if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL)
445        return NULL;
446    if (realpath(path, resolved_path) == NULL) {
447        free(resolved_path);
448        LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
449        return NULL;
450    }
451    /* Safe some memory */
452    char *tmp;
453    if ((tmp = strdup(resolved_path)) == NULL) {
454        free(resolved_path);
455        return NULL;
456    }
457    free(resolved_path);
458    resolved_path = tmp;
459    return resolved_path;
460#endif
461}
462
463/**
464 * Returns pointer to static buffer with basename of path
465 **/
466const char *basename_safe(const char *path)
467{
468    static char buf[MAXPATHLEN+1];
469    strlcpy(buf, path, MAXPATHLEN);
470    return basename(buf);
471}
472
473/**
474 * extended strtok allows the quoted strings
475 * modified strtok.c in glibc 2.0.6
476 **/
477char *strtok_quote(char *s, const char *delim)
478{
479    static char *olds = NULL;
480    char *token;
481
482    if (s == NULL)
483        s = olds;
484
485    /* Scan leading delimiters.  */
486    s += strspn (s, delim);
487    if (*s == '\0')
488        return NULL;
489
490    /* Find the end of the token.  */
491    token = s;
492
493    if (token[0] == '\"') {
494        token++;
495        s = strpbrk (token, "\"");
496    } else {
497        s = strpbrk (token, delim);
498    }
499
500    if (s == NULL) {
501        /* This token finishes the string.  */
502        olds = strchr (token, '\0');
503    } else {
504        /* Terminate the token and make OLDS point past it.  */
505        *s = '\0';
506        olds = s + 1;
507    }
508    return token;
509}
510
511int set_groups(AFPObj *obj, struct passwd *pwd)
512{
513    if (initgroups(pwd->pw_name, pwd->pw_gid) < 0)
514        LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno));
515
516    if ((obj->ngroups = getgroups(0, NULL)) < 0) {
517        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
518        return -1;
519    }
520
521    if (obj->groups)
522        free(obj->groups);
523    if (NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) {
524        LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups);
525        return -1;
526    }
527
528    if ((obj->ngroups = getgroups(obj->ngroups, obj->groups)) < 0 ) {
529        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
530        return -1;
531    }
532
533    return 0;
534}
535
536#define GROUPSTR_BUFSIZE 1024
537const char *print_groups(int ngroups, gid_t *groups)
538{
539    static char groupsstr[GROUPSTR_BUFSIZE];
540    int i;
541    char *s = groupsstr;
542
543    if (ngroups == 0)
544        return "-";
545
546    for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) {
547        s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]);
548    }
549
550    return groupsstr;
551}
552