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. */
35static struct list_head modules = ATALK_LIST_HEAD_INIT(modules);
36
37static sigset_t sigblockset;
38static 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. */
43void 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/* --------------- */
60static 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 */
94struct _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/* ------------------- */
165static void block_signal(uint32_t flags)
166{
167    if ((flags & CNID_FLAG_BLOCK)) {
168        pthread_sigmask(SIG_BLOCK, &sigblockset, NULL);
169    }
170}
171
172/* ------------------- */
173static 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*/
184static 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(). */
201void 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/* --------------- */
217cnid_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/* --------------- */
232int cnid_delete(struct _cnid_db *cdb, cnid_t id)
233{
234int 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/* --------------- */
244cnid_t cnid_get(struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len)
245{
246cnid_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/* --------------- */
255int cnid_getstamp(struct _cnid_db *cdb,  void *buffer, const size_t len)
256{
257cnid_t ret;
258time_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/* --------------- */
276cnid_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/* --------------- */
288int 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/* --------------- */
304char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
305{
306char *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/* --------------- */
319int 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{
322int 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/* --------------- */
331cnid_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{
334cnid_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}
341
342/* --------------- */
343int cnid_wipe(struct _cnid_db *cdb)
344{
345    int ret = 0;
346
347    block_signal(cdb->flags);
348    if (cdb->cnid_wipe)
349        ret = cdb->cnid_wipe(cdb);
350    unblock_signal(cdb->flags);
351    return ret;
352}
353