]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cnid.c
Merge branch-2-1
[netatalk.git] / libatalk / cnid / cnid.c
1 /* 
2  * Copyright (c) 2003 the Netatalk Team
3  * Copyright (c) 2003 Rafal Lewczuk <rlewczuk@pronet.pl>
4  * 
5  * This program is free software; you can redistribute and/or modify
6  * it under the terms of the GNU General Public License as published
7  * by the Free Software Foundation version 2 of the License or later
8  * version if explicitly stated by any of above copyright holders.
9  *
10  */
11 #define USE_LIST
12
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif /* HAVE_CONFIG_H */
16
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/time.h>
20 #include <sys/param.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <signal.h>
25 #include <unistd.h>
26 #include <errno.h>
27
28 #include <atalk/cnid.h>
29 #include <atalk/list.h>
30 #include <atalk/logger.h>
31 #include <atalk/util.h>
32
33 /* List of all registered modules. */
34 static struct list_head modules = ATALK_LIST_HEAD_INIT(modules);
35
36 static sigset_t sigblockset;
37 static const struct itimerval none = {{0, 0}, {0, 0}};
38
39 /* Registers new CNID backend module. */
40
41 /* Once module has been registered, it cannot be unregistered. */
42 void cnid_register(struct _cnid_module *module)
43 {
44     struct list_head *ptr;
45
46     /* Check if our module is already registered. */
47     list_for_each(ptr, &modules)
48         if (0 == strcmp(list_entry(ptr, cnid_module, db_list)->name, module->name)) {
49         LOG(log_error, logtype_afpd, "Module with name [%s] is already registered !", module->name);
50         return;
51     }
52
53     LOG(log_info, logtype_afpd, "Registering CNID module [%s]", module->name);
54     ptr = &(module->db_list);
55     list_add_tail(ptr, &modules);
56 }
57
58 /* --------------- */
59 static int cnid_dir(const char *dir, mode_t mask)
60 {
61    struct stat st, st1; 
62    char tmp[MAXPATHLEN];
63
64    if (stat(dir, &st) < 0) {
65        if (errno != ENOENT) 
66            return -1;
67        if (ad_stat( dir, &st) < 0)
68           return -1;
69
70        LOG(log_info, logtype_cnid, "Setting uid/gid to %d/%d", st.st_uid, st.st_gid);
71        if (setegid(st.st_gid) < 0 || seteuid(st.st_uid) < 0) {
72            LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
73            return -1;
74        }
75
76        if (mkdir(dir, 0777 & ~mask) < 0)
77            return -1;
78    } else {
79        strlcpy(tmp, dir, sizeof(tmp));
80        strlcat(tmp, "/.AppleDB", sizeof(tmp));
81        if (stat(tmp, &st1) < 0) /* use .AppleDB owner, if folder already exists */
82            st1 = st; 
83        LOG(log_info, logtype_cnid, "Setting uid/gid to %d/%d", st1.st_uid, st1.st_gid);
84        if (setegid(st1.st_gid) < 0 || seteuid(st1.st_uid) < 0) {
85            LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
86            return -1;
87        }
88    }
89    return 0;
90 }
91
92 /* Opens CNID database using particular back-end */
93 struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int flags,
94                            const char *cnidsrv, const char *cnidport)
95 {
96     struct _cnid_db *db;
97     cnid_module *mod = NULL;
98     struct list_head *ptr;
99     uid_t uid = -1;  
100     gid_t gid = -1;
101
102     list_for_each(ptr, &modules) {
103         if (0 == strcmp(list_entry(ptr, cnid_module, db_list)->name, type)) {
104             mod = list_entry(ptr, cnid_module, db_list);
105         break;
106         }
107     }
108
109     if (NULL == mod) {
110         LOG(log_error, logtype_afpd, "Cannot find module named [%s] in registered module list!", type);
111         return NULL;
112     }
113
114     if ((mod->flags & CNID_FLAG_SETUID) && !(flags & CNID_FLAG_MEMORY)) {
115         uid = geteuid();
116         gid = getegid();
117         if (seteuid(0)) {
118             LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
119             return NULL;
120         }
121         if (cnid_dir(volpath, mask) < 0) {
122             if ( setegid(gid) < 0 || seteuid(uid) < 0) {
123                 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
124                 exit(EXITERR_SYS);
125             }
126             return NULL;
127         }
128     }
129
130     struct cnid_open_args args = {volpath, mask, flags, cnidsrv, cnidport};
131     db = mod->cnid_open(&args);
132
133     if ((mod->flags & CNID_FLAG_SETUID) && !(flags & CNID_FLAG_MEMORY)) {
134         seteuid(0);
135         if ( setegid(gid) < 0 || seteuid(uid) < 0) {
136             LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
137             exit(EXITERR_SYS);
138         }
139     }
140
141     if (NULL == db) {
142         LOG(log_error, logtype_afpd, "Cannot open CNID db at [%s].", volpath);
143         return NULL;
144     }
145     /* FIXME should module know about it ? */
146     if ((flags & CNID_FLAG_NODEV)) {
147         db->flags |= CNID_FLAG_NODEV;
148     }
149     db->flags |= mod->flags;
150
151     if ((db->flags & CNID_FLAG_BLOCK)) {
152         sigemptyset(&sigblockset);
153         sigaddset(&sigblockset, SIGTERM);
154         sigaddset(&sigblockset, SIGHUP);
155         sigaddset(&sigblockset, SIGUSR1);
156         sigaddset(&sigblockset, SIGUSR2);
157         sigaddset(&sigblockset, SIGALRM);
158     }
159
160     return db;
161 }
162
163 /* ------------------- */
164 static void block_signal( u_int32_t flags)
165 {
166     if ((flags & CNID_FLAG_BLOCK)) {
167         pthread_sigmask(SIG_BLOCK, &sigblockset, NULL);
168     }
169 }
170
171 /* ------------------- */
172 static void unblock_signal(u_int32_t flags)
173 {
174     if ((flags & CNID_FLAG_BLOCK)) {
175         pthread_sigmask(SIG_UNBLOCK, &sigblockset, NULL);
176     }
177 }
178
179 /* ------------------- 
180   protect against bogus value from the DB.
181   adddir really doesn't like 2
182 */
183 static cnid_t valide(cnid_t id)
184 {
185   if (id == CNID_INVALID)
186       return id;
187       
188   if (id < CNID_START) {
189     static int err = 0;
190     if (!err) {
191         err = 1;
192         LOG(log_error, logtype_afpd, "Error: Invalid cnid, corrupted DB?");
193     }
194     return CNID_INVALID;
195   }
196   return id;
197 }
198
199 /* Closes CNID database. Currently it's just a wrapper around db->cnid_close(). */
200 void cnid_close(struct _cnid_db *db)
201 {
202 u_int32_t flags;
203
204     if (NULL == db) {
205         LOG(log_error, logtype_afpd, "Error: cnid_close called with NULL argument !");
206         return;
207     }
208     /* cnid_close free db */
209     flags = db->flags;
210     block_signal(flags);
211     db->cnid_close(db);
212     unblock_signal(flags);
213 }
214
215 /* --------------- */
216 cnid_t cnid_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did, 
217                 const char *name, const size_t len, cnid_t hint)
218 {
219 cnid_t ret;
220
221     block_signal(cdb->flags);
222     ret = valide(cdb->cnid_add(cdb, st, did, name, len, hint));
223     unblock_signal(cdb->flags);
224     return ret;
225 }
226
227 /* --------------- */
228 int cnid_delete(struct _cnid_db *cdb, cnid_t id)
229 {
230 int ret;
231
232     block_signal(cdb->flags);
233     ret = cdb->cnid_delete(cdb, id);
234     unblock_signal(cdb->flags);
235     return ret;
236 }
237
238
239 /* --------------- */
240 cnid_t cnid_get(struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len)
241 {
242 cnid_t ret;
243
244     block_signal(cdb->flags);
245     ret = valide(cdb->cnid_get(cdb, did, name, len));
246     unblock_signal(cdb->flags);
247     return ret;
248 }
249
250 /* --------------- */
251 int cnid_getstamp(struct _cnid_db *cdb,  void *buffer, const size_t len)
252 {
253 cnid_t ret;
254 time_t t;
255
256     if (!cdb->cnid_getstamp) {
257         memset(buffer, 0, len);
258         /* return the current time. it will invalide cache */
259         if (len < sizeof(time_t))
260             return -1;
261         t = time(NULL);
262         memcpy(buffer, &t, sizeof(time_t));
263         return 0;
264     }
265     block_signal(cdb->flags);
266     ret = cdb->cnid_getstamp(cdb, buffer, len);
267     unblock_signal(cdb->flags);
268     return ret;
269 }
270
271 /* --------------- */
272 cnid_t cnid_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
273                    char *name, const size_t len)
274 {
275     cnid_t ret;
276
277     block_signal(cdb->flags);
278     ret = valide(cdb->cnid_lookup(cdb, st, did, name, len));
279     unblock_signal(cdb->flags);
280     return ret;
281 }
282
283 /* --------------- */
284 int cnid_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
285 {
286     int ret;
287     
288     if (cdb->cnid_find == NULL) {
289         LOG(log_error, logtype_cnid, "cnid_find not supported by CNID backend");        
290         return -1;
291     }
292
293     block_signal(cdb->flags);
294     ret = cdb->cnid_find(cdb, name, namelen, buffer, buflen);
295     unblock_signal(cdb->flags);
296     return ret;
297 }
298
299 /* --------------- */
300 char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
301 {
302 char *ret;
303
304     block_signal(cdb->flags);
305     ret = cdb->cnid_resolve(cdb, id, buffer, len);
306     unblock_signal(cdb->flags);
307     if (ret && !strcmp(ret, "..")) {
308         LOG(log_error, logtype_afpd, "cnid_resolve: name is '..', corrupted db? ");
309         ret = NULL;
310     }
311     return ret;
312 }
313
314 /* --------------- */
315 int cnid_update   (struct _cnid_db *cdb, const cnid_t id, const struct stat *st, 
316                         const cnid_t did, char *name, const size_t len)
317 {
318 int ret;
319
320     block_signal(cdb->flags);
321     ret = cdb->cnid_update(cdb, id, st, did, name, len);
322     unblock_signal(cdb->flags);
323     return ret;
324 }
325                         
326 /* --------------- */
327 cnid_t cnid_rebuild_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
328                        char *name, const size_t len, cnid_t hint)
329 {
330 cnid_t ret;
331
332     block_signal(cdb->flags);
333     ret = cdb->cnid_rebuild_add(cdb, st, did, name, len, hint);
334     unblock_signal(cdb->flags);
335     return ret;
336 }