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. */ 34static struct list_head modules = ATALK_LIST_HEAD_INIT(modules); 35 36static sigset_t sigblockset; 37static 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. */ 42void 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/* --------------- */ 59static 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 */ 93struct _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/* ------------------- */ 164static 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/* ------------------- */ 172static 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*/ 183static 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(). */ 200void cnid_close(struct _cnid_db *db) 201{ 202u_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/* --------------- */ 216cnid_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{ 219cnid_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/* --------------- */ 228int cnid_delete(struct _cnid_db *cdb, cnid_t id) 229{ 230int 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/* --------------- */ 240cnid_t cnid_get(struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len) 241{ 242cnid_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/* --------------- */ 251int cnid_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len) 252{ 253cnid_t ret; 254time_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/* --------------- */ 272cnid_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/* --------------- */ 284int 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/* --------------- */ 300char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len) 301{ 302char *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/* --------------- */ 315int 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{ 318int 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/* --------------- */ 327cnid_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{ 330cnid_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} 337