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