]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cnid.c
add SIGUSR2 (send a message) to the blocked signals list for cnid
[netatalk.git] / libatalk / cnid / cnid.c
1 /* 
2  * $Id: cnid.c,v 1.6 2009-11-19 06:40:51 didg 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 static struct itimerval savetimer;
41
42 /* Registers new CNID backend module. */
43
44 /* Once module has been registered, it cannot be unregistered. */
45 void cnid_register(struct _cnid_module *module)
46 {
47     struct list_head *ptr;
48
49     /* Check if our module is already registered. */
50     list_for_each(ptr, &modules)
51         if (0 == strcmp(list_entry(ptr, cnid_module, db_list)->name, module->name)) {
52         LOG(log_error, logtype_afpd, "Module with name [%s] is already registered !", module->name);
53         return;
54     }
55
56     LOG(log_info, logtype_afpd, "Registering CNID module [%s]", module->name);
57     ptr = &(module->db_list);
58     list_add_tail(ptr, &modules);
59 }
60
61 /* --------------- */
62 static int cnid_dir(const char *dir, mode_t mask)
63 {
64    struct stat st, st1; 
65    char tmp[MAXPATHLEN];
66
67    if (stat(dir, &st) < 0) {
68        if (errno != ENOENT) 
69            return -1;
70        if (ad_stat( dir, &st) < 0)
71           return -1;
72
73        LOG(log_info, logtype_cnid, "Setting uid/gid to %d/%d", st.st_uid, st.st_gid);
74        if (setegid(st.st_gid) < 0 || seteuid(st.st_uid) < 0) {
75            LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
76            return -1;
77        }
78
79        if (mkdir(dir, 0777 & ~mask) < 0)
80            return -1;
81    } else {
82        strlcpy(tmp, dir, sizeof(tmp));
83        strlcat(tmp, "/.AppleDB", sizeof(tmp));
84        if (stat(tmp, &st1) < 0) /* use .AppleDB owner, if folder already exists */
85            st1 = st; 
86        LOG(log_info, logtype_cnid, "Setting uid/gid to %d/%d", st1.st_uid, st1.st_gid);
87        if (setegid(st1.st_gid) < 0 || seteuid(st1.st_uid) < 0) {
88            LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
89            return -1;
90        }
91    }
92    return 0;
93 }
94
95 /* Opens CNID database using particular back-end */
96 struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int flags)
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)) {
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     db = mod->cnid_open(volpath, mask);
133
134     if ((mod->flags & CNID_FLAG_SETUID)) {
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) {
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( u_int32_t flags)
166 {
167     if ((flags & CNID_FLAG_BLOCK)) {
168         sigprocmask(SIG_BLOCK, &sigblockset, NULL);
169         setitimer(ITIMER_REAL, &none, &savetimer);
170     }
171 }
172
173 /* ------------------- */
174 static void unblock_signal(u_int32_t flags)
175 {
176     if ((flags & CNID_FLAG_BLOCK)) {
177         setitimer(ITIMER_REAL, &savetimer, NULL);
178         sigprocmask(SIG_UNBLOCK, &sigblockset, NULL);
179     }
180 }
181
182 /* Closes CNID database. Currently it's just a wrapper around db->cnid_close(). */
183 void cnid_close(struct _cnid_db *db)
184 {
185 u_int32_t flags;
186
187     if (NULL == db) {
188         LOG(log_error, logtype_afpd, "Error: cnid_close called with NULL argument !");
189         return;
190     }
191     /* cnid_close free db */
192     flags = db->flags;
193     block_signal(flags);
194     db->cnid_close(db);
195     unblock_signal(flags);
196 }
197
198 /* --------------- */
199 cnid_t cnid_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did, 
200                         char *name, const size_t len, cnid_t hint)
201 {
202 cnid_t ret;
203
204     block_signal(cdb->flags);
205     ret = cdb->cnid_add(cdb, st, did, name, len, hint);
206     unblock_signal(cdb->flags);
207     return ret;
208 }
209
210 /* --------------- */
211 int cnid_delete(struct _cnid_db *cdb, cnid_t id)
212 {
213 int ret;
214
215     block_signal(cdb->flags);
216     ret = cdb->cnid_delete(cdb, id);
217     unblock_signal(cdb->flags);
218     return ret;
219 }
220
221
222 /* --------------- */
223 cnid_t cnid_get(struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len)
224 {
225 cnid_t ret;
226
227     block_signal(cdb->flags);
228     ret = cdb->cnid_get(cdb, did, name, len);
229     unblock_signal(cdb->flags);
230     return ret;
231 }
232
233 /* --------------- */
234 int cnid_getstamp(struct _cnid_db *cdb,  void *buffer, const size_t len)
235 {
236 cnid_t ret;
237 time_t t;
238
239     if (!cdb->cnid_getstamp) {
240         memset(buffer, 0, len);
241         /* return the current time. it will invalide cache */
242         if (len < sizeof(time_t))
243             return -1;
244         t = time(NULL);
245         memcpy(buffer, &t, sizeof(time_t));
246         return 0;
247     }
248     block_signal(cdb->flags);
249     ret = cdb->cnid_getstamp(cdb, buffer, len);
250     unblock_signal(cdb->flags);
251     return ret;
252 }
253
254 /* --------------- */
255 cnid_t cnid_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
256                         char *name, const size_t len)
257 {
258 cnid_t ret;
259
260     block_signal(cdb->flags);
261     ret = cdb->cnid_lookup(cdb, st, did, name, len);
262     unblock_signal(cdb->flags);
263     return ret;
264 }
265
266 /* --------------- */
267 char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
268 {
269 char *ret;
270
271     block_signal(cdb->flags);
272     ret = cdb->cnid_resolve(cdb, id, buffer, len);
273     unblock_signal(cdb->flags);
274     if (ret && !strcmp(ret, "..")) {
275         LOG(log_error, logtype_afpd, "cnid_resolve: name is '..', corrupted db? ");
276         ret = NULL;
277     }
278     return ret;
279 }
280
281 /* --------------- */
282 int cnid_update   (struct _cnid_db *cdb, const cnid_t id, const struct stat *st, 
283                         const cnid_t did, char *name, const size_t len)
284 {
285 int ret;
286
287     block_signal(cdb->flags);
288     ret = cdb->cnid_update(cdb, id, st, did, name, len);
289     unblock_signal(cdb->flags);
290     return ret;
291 }
292                         
293 /* --------------- */
294 cnid_t cnid_rebuild_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
295                        char *name, const size_t len, cnid_t hint)
296 {
297 cnid_t ret;
298
299     block_signal(cdb->flags);
300     ret = cdb->cnid_rebuild_add(cdb, st, did, name, len, hint);
301     unblock_signal(cdb->flags);
302     return ret;
303 }