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