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