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{ 219 cnid_t ret; 220 221 if (len == 0) 222 return CNID_INVALID; 223 224 block_signal(cdb->flags); 225 ret = valide(cdb->cnid_add(cdb, st, did, name, len, hint)); 226 unblock_signal(cdb->flags); 227 return ret; 228} 229 230/* --------------- */ 231int cnid_delete(struct _cnid_db *cdb, cnid_t id) 232{ 233int ret; 234 235 block_signal(cdb->flags); 236 ret = cdb->cnid_delete(cdb, id); 237 unblock_signal(cdb->flags); 238 return ret; 239} 240 241 242/* --------------- */ 243cnid_t cnid_get(struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len) 244{ 245cnid_t ret; 246 247 block_signal(cdb->flags); 248 ret = valide(cdb->cnid_get(cdb, did, name, len)); 249 unblock_signal(cdb->flags); 250 return ret; 251} 252 253/* --------------- */ 254int cnid_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len) 255{ 256cnid_t ret; 257time_t t; 258 259 if (!cdb->cnid_getstamp) { 260 memset(buffer, 0, len); 261 /* return the current time. it will invalide cache */ 262 if (len < sizeof(time_t)) 263 return -1; 264 t = time(NULL); 265 memcpy(buffer, &t, sizeof(time_t)); 266 return 0; 267 } 268 block_signal(cdb->flags); 269 ret = cdb->cnid_getstamp(cdb, buffer, len); 270 unblock_signal(cdb->flags); 271 return ret; 272} 273 274/* --------------- */ 275cnid_t cnid_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t did, 276 char *name, const size_t len) 277{ 278 cnid_t ret; 279 280 block_signal(cdb->flags); 281 ret = valide(cdb->cnid_lookup(cdb, st, did, name, len)); 282 unblock_signal(cdb->flags); 283 return ret; 284} 285 286/* --------------- */ 287int cnid_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen) 288{ 289 int ret; 290 291 if (cdb->cnid_find == NULL) { 292 LOG(log_error, logtype_cnid, "cnid_find not supported by CNID backend"); 293 return -1; 294 } 295 296 block_signal(cdb->flags); 297 ret = cdb->cnid_find(cdb, name, namelen, buffer, buflen); 298 unblock_signal(cdb->flags); 299 return ret; 300} 301 302/* --------------- */ 303char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len) 304{ 305char *ret; 306 307 block_signal(cdb->flags); 308 ret = cdb->cnid_resolve(cdb, id, buffer, len); 309 unblock_signal(cdb->flags); 310 if (ret && !strcmp(ret, "..")) { 311 LOG(log_error, logtype_afpd, "cnid_resolve: name is '..', corrupted db? "); 312 ret = NULL; 313 } 314 return ret; 315} 316 317/* --------------- */ 318int cnid_update (struct _cnid_db *cdb, const cnid_t id, const struct stat *st, 319 const cnid_t did, char *name, const size_t len) 320{ 321int ret; 322 323 block_signal(cdb->flags); 324 ret = cdb->cnid_update(cdb, id, st, did, name, len); 325 unblock_signal(cdb->flags); 326 return ret; 327} 328 329/* --------------- */ 330cnid_t cnid_rebuild_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did, 331 char *name, const size_t len, cnid_t hint) 332{ 333cnid_t ret; 334 335 block_signal(cdb->flags); 336 ret = cdb->cnid_rebuild_add(cdb, st, did, name, len, hint); 337 unblock_signal(cdb->flags); 338 return ret; 339} 340