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