]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/unix.c
Test for EAs and ad_convert as root
[netatalk.git] / libatalk / util / unix.c
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
36 #include <atalk/adouble.h>
37 #include <atalk/ea.h>
38 #include <atalk/afp.h>
39 #include <atalk/logger.h>
40 #include <atalk/vfs.h>
41 #include <atalk/util.h>
42 #include <atalk/unix.h>
43 #include <atalk/compat.h>
44
45 /* close all FDs >= a specified value */
46 static void closeall(int fd)
47 {
48     int fdlimit = sysconf(_SC_OPEN_MAX);
49
50     while (fd < fdlimit)
51         close(fd++);
52 }
53
54 /*!
55  * Daemonize
56  *
57  * Fork, exit parent, setsid(), optionally chdir("/"), optionally close all fds
58  *
59  * returns -1 on failure, but you can't do much except exit in that case
60  * since we may already have forked
61  */
62 int daemonize(int nochdir, int noclose)
63 {
64     switch (fork()) {
65     case 0:
66         break;
67     case -1:
68         return -1;
69     default:
70         _exit(0);
71     }
72
73     if (setsid() < 0)
74         return -1;
75
76     switch (fork()) {
77     case 0: 
78         break;
79     case -1:
80         return -1;
81     default:
82         _exit(0);
83     }
84
85     if (!nochdir)
86         chdir("/");
87
88     if (!noclose) {
89         closeall(0);
90         open("/dev/null",O_RDWR);
91         dup(0);
92         dup(0);
93     }
94
95     return 0;
96 }
97
98 static uid_t saved_uid = -1;
99
100 /*
101  * seteuid(0) and back, if either fails and panic != 0 we PANIC
102  */
103 void become_root(void)
104 {
105     if (getuid() == 0) {
106         saved_uid = geteuid();
107         if (seteuid(0) != 0)
108             AFP_PANIC("Can't seteuid(0)");
109     }
110 }
111
112 void unbecome_root(void)
113 {
114     if (getuid() == 0) {
115         if (saved_uid == -1 || seteuid(saved_uid) < 0)
116             AFP_PANIC("Can't seteuid back");
117         saved_uid = -1;
118     }
119 }
120
121 /*!
122  * @brief get cwd in static buffer
123  *
124  * @returns pointer to path or pointer to error messages on error
125  */
126 const char *getcwdpath(void)
127 {
128     static char cwd[MAXPATHLEN + 1];
129     char *p;
130
131     if ((p = getcwd(cwd, MAXPATHLEN)) != NULL)
132         return p;
133     else
134         return strerror(errno);
135 }
136
137 /*!
138  * @brief Request absolute path
139  *
140  * @returns Absolute filesystem path to object
141  */
142 const char *fullpathname(const char *name)
143 {
144     static char wd[MAXPATHLEN + 1];
145
146     if (name[0] == '/')
147         return name;
148
149     if (getcwd(wd , MAXPATHLEN)) {
150         strlcat(wd, "/", MAXPATHLEN);
151         strlcat(wd, name, MAXPATHLEN);
152     } else {
153         strlcpy(wd, name, MAXPATHLEN);
154     }
155
156     return wd;
157 }
158
159 /*!
160  * Takes a buffer with a path, strips slashs, returns basename
161  *
162  * @param p (rw) path
163  *        path may be
164  *          "[/][dir/[...]]file"
165  *        or
166  *          "[/][dir/[...]]dir/[/]"
167  *        Result is "file" or "dir" 
168  *
169  * @returns pointer to basename in path buffer, buffer is possibly modified
170  */
171 char *stripped_slashes_basename(char *p)
172 {
173     int i = strlen(p) - 1;
174     while (i > 0 && p[i] == '/')
175         p[i--] = 0;
176     return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
177 }
178
179 /*!
180  * @brief symlink safe chdir replacement
181  *
182  * Only chdirs to dir if it doesn't contain symlinks.
183  *
184  * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
185  */
186 int lchdir(const char *dir)
187 {
188     char buf[MAXPATHLEN+1];
189     char cwd[MAXPATHLEN+1];
190     char *test;
191     int  i;
192
193     /*
194      dir is a canonical path (without "../" "./" "//" )
195      but may end with a / 
196     */
197     *cwd = 0;
198     if (*dir != '/') {
199         if (getcwd(cwd, MAXPATHLEN) == NULL)
200             return -1;
201     }
202     if (chdir(dir) != 0)
203         return -1;
204
205     /* 
206      * Cases:
207      * chdir request   | realpath result | ret
208      * (after getwcwd) |                 |
209      * =======================================
210      * /a/b/.          | /a/b            | 0
211      * /a/b/.          | /c              | 1
212      * /a/b/.          | /c/d/e/f        | 1
213      */
214     if (getcwd(buf, MAXPATHLEN) == NULL)
215         return 1;
216
217     i = 0;
218     if (*cwd) {
219         /* relative path requested, 
220          * Same directory?
221         */
222         for (; cwd[i]; i++) {
223             if (buf[i] != cwd[i])
224                 return 1;
225         }
226         if (buf[i]) {
227             if (buf[i] != '/')
228                 return 1;
229             i++;
230         }                    
231     }
232
233     test = &buf[i];    
234     for (i = 0; test[i]; i++) {
235         if (test[i] != dir[i]) {
236             return 1;
237         }
238     }
239     /* trailing '/' ? */
240     if (!dir[i])
241         return 0;
242
243     if (dir[i] != '/')
244         return 1;
245
246     i++;
247     if (dir[i])
248         return 1;
249
250     return 0;
251 }
252
253 /*!
254  * Store n random bytes an buf
255  */
256 void randombytes(void *buf, int n)
257 {
258     char *p = (char *)buf;
259     int fd, i;
260     struct timeval tv;
261
262     if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
263         /* generate from /dev/urandom */
264         if (read(fd, buf, n) != n) {
265             close(fd);
266             fd = -1;
267         } else {
268             close(fd);
269             /* fd now != -1, so srandom wont be called below */
270         }
271     }
272
273     if (fd == -1) {
274         gettimeofday(&tv, NULL);
275         srandom((unsigned int)tv.tv_usec);
276         for (i=0 ; i < n ; i++)
277             p[i] = random() & 0xFF;
278     }
279
280     return;
281 }
282
283 int gmem(gid_t gid, int ngroups, gid_t *groups)
284 {
285     int         i;
286
287     for ( i = 0; i < ngroups; i++ ) {
288         if ( groups[ i ] == gid ) {
289             return( 1 );
290         }
291     }
292     return( 0 );
293 }