1/* 2 * Copyright (c) 1990,1993 Regents of The University of Michigan. 3 * All Rights Reserved. See COPYRIGHT. 4 */ 5 6#ifdef HAVE_CONFIG_H 7#include "config.h" 8#endif /* HAVE_CONFIG_H */ 9 10#include <stdio.h> 11#include <stdlib.h> 12#include <ctype.h> 13#include <pwd.h> 14#include <grp.h> 15#include <utime.h> 16#include <errno.h> 17#ifdef HAVE_STRINGS_H 18#include <strings.h> 19#endif 20/* STDC check */ 21#if STDC_HEADERS 22#include <string.h> 23#else /* STDC_HEADERS */ 24#ifndef HAVE_STRCHR 25#define strchr index 26#define strrchr index 27#endif /* HAVE_STRCHR */ 28char *strchr (), *strrchr (); 29#ifndef HAVE_MEMCPY 30#define memcpy(d,s,n) bcopy ((s), (d), (n)) 31#define memmove(d,s,n) bcopy ((s), (d), (n)) 32#endif /* ! HAVE_MEMCPY */ 33#endif /* STDC_HEADERS */ 34#include <sys/param.h> 35#include <sys/socket.h> 36#include <netinet/in.h> 37#include <arpa/inet.h> 38#include <inttypes.h> 39#include <time.h> 40 41#include <atalk/asp.h> 42#include <atalk/dsi.h> 43#include <atalk/adouble.h> 44#include <atalk/afp.h> 45#include <atalk/util.h> 46#include <atalk/volinfo.h> 47#include <atalk/logger.h> 48#include <atalk/vfs.h> 49#include <atalk/uuid.h> 50#include <atalk/bstrlib.h> 51#include <atalk/bstradd.h> 52#include <atalk/ftw.h> 53#include <atalk/globals.h> 54#include <atalk/fce_api.h> 55#include <atalk/errchk.h> 56 57#ifdef CNID_DB 58#include <atalk/cnid.h> 59#endif /* CNID_DB*/ 60 61#include "directory.h" 62#include "file.h" 63#include "volume.h" 64#include "unix.h" 65#include "mangle.h" 66#include "fork.h" 67#include "hash.h" 68#include "acls.h" 69#include "auth.h" 70 71extern int afprun(int root, char *cmd, int *outfd); 72 73#ifndef MIN 74#define MIN(a, b) ((a) < (b) ? (a) : (b)) 75#endif /* ! MIN */ 76 77#ifndef UUID_PRINTABLE_STRING_LENGTH 78#define UUID_PRINTABLE_STRING_LENGTH 37 79#endif 80 81/* Globals */ 82struct vol *current_vol; /* last volume from getvolbyvid() */ 83 84static struct vol *Volumes = NULL; 85static u_int16_t lastvid = 0; 86static char *Trash = "\02\024Network Trash Folder"; 87 88static struct extmap *Extmap = NULL, *Defextmap = NULL; 89static int Extmap_cnt; 90static void free_extmap(void); 91 92#define VOLOPT_ALLOW 0 /* user allow list */ 93#define VOLOPT_DENY 1 /* user deny list */ 94#define VOLOPT_RWLIST 2 /* user rw list */ 95#define VOLOPT_ROLIST 3 /* user ro list */ 96#define VOLOPT_PASSWORD 4 /* volume password */ 97#define VOLOPT_CASEFOLD 5 /* character case mangling */ 98#define VOLOPT_FLAGS 6 /* various flags */ 99#define VOLOPT_DBPATH 7 /* path to database */ 100#define VOLOPT_LIMITSIZE 8 /* Limit the size of the volume */ 101/* Usable slot: 9 */ 102#define VOLOPT_VETO 10 /* list of veto filespec */ 103#define VOLOPT_PREEXEC 11 /* preexec command */ 104#define VOLOPT_ROOTPREEXEC 12 /* root preexec command */ 105#define VOLOPT_POSTEXEC 13 /* postexec command */ 106#define VOLOPT_ROOTPOSTEXEC 14 /* root postexec command */ 107#define VOLOPT_ENCODING 15 /* mac encoding (pre OSX)*/ 108#define VOLOPT_MACCHARSET 16 109#define VOLOPT_CNIDSCHEME 17 110#define VOLOPT_ADOUBLE 18 /* adouble version */ 111 112#ifdef FORCE_UIDGID 113#warning UIDGID 114#include "uid.h" 115 116#define VOLOPT_FORCEUID 19 /* force uid for username x */ 117#define VOLOPT_FORCEGID 20 /* force gid for group x */ 118#endif /* FORCE_UIDGID */ 119 120#define VOLOPT_UMASK 21 121#define VOLOPT_ALLOWED_HOSTS 22 122#define VOLOPT_DENIED_HOSTS 23 123#define VOLOPT_DPERM 24 /* dperm default directories perms */ 124#define VOLOPT_FPERM 25 /* fperm default files perms */ 125#define VOLOPT_DFLTPERM 26 /* perm */ 126#define VOLOPT_EA_VFS 27 /* Extended Attributes vfs indirection */ 127#define VOLOPT_CNIDSERVER 28 /* CNID Server ip address*/ 128#define VOLOPT_CNIDPORT 30 /* CNID server tcp port */ 129 130#define VOLOPT_MAX 31 /* <== IMPORTANT !!!!!! */ 131#define VOLOPT_NUM (VOLOPT_MAX + 1) 132 133#define VOLPASSLEN 8 134#define VOLOPT_DEFAULT ":DEFAULT:" 135#define VOLOPT_DEFAULT_LEN 9 136 137struct vol_option { 138 char *c_value; 139 int i_value; 140}; 141 142typedef struct _special_folder { 143 const char *name; 144 int precreate; 145 mode_t mode; 146 int hide; 147} _special_folder; 148 149static const _special_folder special_folders[] = { 150 {"Network Trash Folder", 1, 0777, 1}, 151 {"Temporary Items", 1, 0777, 1}, 152 {".AppleDesktop", 1, 0777, 0}, 153#if 0 154 {"TheFindByContentFolder", 0, 0, 1}, 155 {"TheVolumeSettingsFolder", 0, 0, 1}, 156#endif 157 {NULL, 0, 0, 0}}; 158 159/* Forward declarations */ 160static void handle_special_folders (const struct vol *); 161static void deletevol(struct vol *vol); 162static void volume_free(struct vol *vol); 163static void check_ea_sys_support(struct vol *vol); 164static char *get_vol_uuid(const AFPObj *obj, const char *volname); 165static int readvolfile(AFPObj *obj, struct afp_volume_name *p1,char *p2, int user, struct passwd *pwent); 166 167static void volfree(struct vol_option *options, const struct vol_option *save) 168{ 169 int i; 170 171 if (save) { 172 for (i = 0; i < VOLOPT_MAX; i++) { 173 if (options[i].c_value && (options[i].c_value != save[i].c_value)) 174 free(options[i].c_value); 175 } 176 } else { 177 for (i = 0; i < VOLOPT_MAX; i++) { 178 if (options[i].c_value) 179 free(options[i].c_value); 180 } 181 } 182} 183 184 185#define is_var(a, b) (strncmp((a), (b), 2) == 0) 186 187/* 188 * Handle variable substitutions. here's what we understand: 189 * $b -> basename of path 190 * $c -> client ip/appletalk address 191 * $d -> volume pathname on server 192 * $f -> full name (whatever's in the gecos field) 193 * $g -> group 194 * $h -> hostname 195 * $i -> client ip/appletalk address without port 196 * $s -> server name (hostname if it doesn't exist) 197 * $u -> username (guest is usually nobody) 198 * $v -> volume name or basename if null 199 * $z -> zone (may not exist) 200 * $$ -> $ 201 * 202 * This get's called from readvolfile with 203 * path = NULL, volname = NULL for xlating the volumes path 204 * path = path, volname = NULL for xlating the volumes name 205 * ... and from volumes options parsing code when xlating eg dbpath with 206 * path = path, volname = volname 207 * 208 * Using this information we can reject xlation of any variable depeninding on a login 209 * context which is not given in the afp master, where we must evaluate this whole stuff 210 * too for the Zeroconf announcements. 211 */ 212static char *volxlate(AFPObj *obj, 213 char *dest, 214 size_t destlen, 215 char *src, 216 struct passwd *pwd, 217 char *path, 218 char *volname) 219{ 220 char *p, *r; 221 const char *q; 222 int len; 223 char *ret; 224 int afpmaster = 0; 225 int xlatevolname = 0; 226 227 if (parent_or_child == 0) 228 afpmaster = 1; 229 230 if (path && !volname) 231 /* cf above */ 232 xlatevolname = 1; 233 234 if (!src) { 235 return NULL; 236 } 237 if (!dest) { 238 dest = calloc(destlen +1, 1); 239 } 240 ret = dest; 241 if (!ret) { 242 return NULL; 243 } 244 strlcpy(dest, src, destlen +1); 245 if ((p = strchr(src, '$')) == NULL) /* nothing to do */ 246 return ret; 247 248 /* first part of the path. just forward to the next variable. */ 249 len = MIN((size_t)(p - src), destlen); 250 if (len > 0) { 251 destlen -= len; 252 dest += len; 253 } 254 255 while (p && destlen > 0) { 256 /* now figure out what the variable is */ 257 q = NULL; 258 if (is_var(p, "$b")) { 259 if (afpmaster && xlatevolname) 260 return NULL; 261 if (path) { 262 if ((q = strrchr(path, '/')) == NULL) 263 q = path; 264 else if (*(q + 1) != '\0') 265 q++; 266 } 267 } else if (is_var(p, "$c")) { 268 if (afpmaster && xlatevolname) 269 return NULL; 270#ifndef NO_DDP 271 if (obj->proto == AFPPROTO_ASP) { 272 ASP asp = obj->handle; 273 274 len = sprintf(dest, "%u.%u", ntohs(asp->asp_sat.sat_addr.s_net), 275 asp->asp_sat.sat_addr.s_node); 276 dest += len; 277 destlen -= len; 278 279 } 280#endif 281 if (obj->proto == AFPPROTO_DSI) { 282 DSI *dsi = obj->handle; 283 len = sprintf(dest, "%s:%u", 284 getip_string((struct sockaddr *)&dsi->client), 285 getip_port((struct sockaddr *)&dsi->client)); 286 dest += len; 287 destlen -= len; 288 } 289 } else if (is_var(p, "$d")) { 290 if (afpmaster && xlatevolname) 291 return NULL; 292 q = path; 293 } else if (pwd && is_var(p, "$f")) { 294 if (afpmaster && xlatevolname) 295 return NULL; 296 if ((r = strchr(pwd->pw_gecos, ','))) 297 *r = '\0'; 298 q = pwd->pw_gecos; 299 } else if (pwd && is_var(p, "$g")) { 300 if (afpmaster && xlatevolname) 301 return NULL; 302 struct group *grp = getgrgid(pwd->pw_gid); 303 if (grp) 304 q = grp->gr_name; 305 } else if (is_var(p, "$h")) { 306 q = obj->options.hostname; 307 } else if (is_var(p, "$i")) { 308 if (afpmaster && xlatevolname) 309 return NULL; 310#ifndef NO_DDP 311 if (obj->proto == AFPPROTO_ASP) { 312 ASP asp = obj->handle; 313 len = sprintf(dest, "%u", ntohs(asp->asp_sat.sat_addr.s_net)); 314 dest += len; 315 destlen -= len; 316 317 } 318#endif 319 if (obj->proto == AFPPROTO_DSI) { 320 DSI *dsi = obj->handle; 321 q = getip_string((struct sockaddr *)&dsi->client); 322 } 323 } else if (is_var(p, "$s")) { 324 if (obj->Obj) 325 q = obj->Obj; 326 else if (obj->options.server) { 327 q = obj->options.server; 328 } else 329 q = obj->options.hostname; 330 } else if (obj->username && is_var(p, "$u")) { 331 if (afpmaster && xlatevolname) 332 return NULL; 333 char* sep = NULL; 334 if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL) 335 q = sep+1; 336 else 337 q = obj->username; 338 } else if (is_var(p, "$v")) { 339 if (afpmaster && xlatevolname) 340 return NULL; 341 if (volname) { 342 q = volname; 343 } 344 else if (path) { 345 if ((q = strrchr(path, '/')) == NULL) 346 q = path; 347 else if (*(q + 1) != '\0') 348 q++; 349 } 350 } else if (is_var(p, "$z")) { 351 q = obj->Zone; 352 } else if (is_var(p, "$$")) { 353 q = "$"; 354 } else 355 q = p; 356 357 /* copy the stuff over. if we don't understand something that we 358 * should, just skip it over. */ 359 if (q) { 360 len = MIN(p == q ? 2 : strlen(q), destlen); 361 strncpy(dest, q, len); 362 dest += len; 363 destlen -= len; 364 } 365 366 /* stuff up to next $ */ 367 src = p + 2; 368 p = strchr(src, '$'); 369 len = p ? MIN((size_t)(p - src), destlen) : destlen; 370 if (len > 0) { 371 strncpy(dest, src, len); 372 dest += len; 373 destlen -= len; 374 } 375 } 376 return ret; 377} 378 379/* to make sure that val is valid, make sure to select an opt that 380 includes val */ 381static int optionok(const char *buf, const char *opt, const char *val) 382{ 383 if (!strstr(buf,opt)) 384 return 0; 385 if (!val[1]) 386 return 0; 387 return 1; 388} 389 390 391/* -------------------- */ 392static void setoption(struct vol_option *options, struct vol_option *save, int opt, const char *val) 393{ 394 if (options[opt].c_value && (!save || options[opt].c_value != save[opt].c_value)) 395 free(options[opt].c_value); 396 options[opt].c_value = strdup(val + 1); 397} 398 399/* ------------------------------------------ 400 handle all the options. tmp can't be NULL. */ 401static void volset(struct vol_option *options, struct vol_option *save, 402 char *volname, int vlen, 403 const char *tmp) 404{ 405 char *val; 406 407 val = strchr(tmp, ':'); 408 if (!val) { 409 /* we'll assume it's a volume name. */ 410 strncpy(volname, tmp, vlen); 411 volname[vlen] = 0; 412 return; 413 } 414#if 0 415 LOG(log_debug, logtype_afpd, "Parsing volset %s", val); 416#endif 417 if (optionok(tmp, "allow:", val)) { 418 setoption(options, save, VOLOPT_ALLOW, val); 419 420 } else if (optionok(tmp, "deny:", val)) { 421 setoption(options, save, VOLOPT_DENY, val); 422 423 } else if (optionok(tmp, "rwlist:", val)) { 424 setoption(options, save, VOLOPT_RWLIST, val); 425 426 } else if (optionok(tmp, "rolist:", val)) { 427 setoption(options, save, VOLOPT_ROLIST, val); 428 429 } else if (optionok(tmp, "codepage:", val)) { 430 LOG (log_error, logtype_afpd, "The old codepage system has been removed. Please make sure to read the documentation !!!!"); 431 /* Make sure we don't screw anything */ 432 exit (EXITERR_CONF); 433 } else if (optionok(tmp, "volcharset:", val)) { 434 setoption(options, save, VOLOPT_ENCODING, val); 435 } else if (optionok(tmp, "maccharset:", val)) { 436 setoption(options, save, VOLOPT_MACCHARSET, val); 437 } else if (optionok(tmp, "veto:", val)) { 438 setoption(options, save, VOLOPT_VETO, val); 439 } else if (optionok(tmp, "cnidscheme:", val)) { 440 setoption(options, save, VOLOPT_CNIDSCHEME, val); 441 } else if (optionok(tmp, "casefold:", val)) { 442 if (strcasecmp(val + 1, "tolower") == 0) 443 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER; 444 else if (strcasecmp(val + 1, "toupper") == 0) 445 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER; 446 else if (strcasecmp(val + 1, "xlatelower") == 0) 447 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER; 448 else if (strcasecmp(val + 1, "xlateupper") == 0) 449 options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER; 450 } else if (optionok(tmp, "adouble:", val)) { 451 if (strcasecmp(val + 1, "v1") == 0) 452 options[VOLOPT_ADOUBLE].i_value = AD_VERSION1; 453#if AD_VERSION == AD_VERSION2 454 else if (strcasecmp(val + 1, "v2") == 0) 455 options[VOLOPT_ADOUBLE].i_value = AD_VERSION2; 456 else if (strcasecmp(val + 1, "osx") == 0) 457 options[VOLOPT_ADOUBLE].i_value = AD_VERSION2_OSX; 458 else if (strcasecmp(val + 1, "sfm") == 0) 459 options[VOLOPT_ADOUBLE].i_value = AD_VERSION1_SFM; 460#endif 461 } else if (optionok(tmp, "options:", val)) { 462 char *p; 463 464 if ((p = strtok(val + 1, ",")) == NULL) /* nothing */ 465 return; 466 467 while (p) { 468 if (strcasecmp(p, "prodos") == 0) 469 options[VOLOPT_FLAGS].i_value |= AFPVOL_A2VOL; 470 else if (strcasecmp(p, "mswindows") == 0) { 471 options[VOLOPT_FLAGS].i_value |= AFPVOL_MSWINDOWS | AFPVOL_USEDOTS; 472 } else if (strcasecmp(p, "crlf") == 0) 473 options[VOLOPT_FLAGS].i_value |= AFPVOL_CRLF; 474 else if (strcasecmp(p, "noadouble") == 0) 475 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOADOUBLE; 476 else if (strcasecmp(p, "ro") == 0) 477 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO; 478 else if (strcasecmp(p, "nohex") == 0) 479 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX; 480 else if (strcasecmp(p, "usedots") == 0) 481 options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS; 482 else if (strcasecmp(p, "invisibledots") == 0) 483 options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS | AFPVOL_INV_DOTS; 484 else if (strcasecmp(p, "limitsize") == 0) 485 options[VOLOPT_FLAGS].i_value |= AFPVOL_LIMITSIZE; 486 /* support for either "dropbox" or "dropkludge" */ 487 else if (strcasecmp(p, "dropbox") == 0) 488 options[VOLOPT_FLAGS].i_value |= AFPVOL_DROPBOX; 489 else if (strcasecmp(p, "dropkludge") == 0) 490 options[VOLOPT_FLAGS].i_value |= AFPVOL_DROPBOX; 491 else if (strcasecmp(p, "nofileid") == 0) 492 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOFILEID; 493 else if (strcasecmp(p, "nostat") == 0) 494 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOSTAT; 495 else if (strcasecmp(p, "preexec_close") == 0) 496 options[VOLOPT_PREEXEC].i_value = 1; 497 else if (strcasecmp(p, "root_preexec_close") == 0) 498 options[VOLOPT_ROOTPREEXEC].i_value = 1; 499 else if (strcasecmp(p, "upriv") == 0) 500 options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV; 501 else if (strcasecmp(p, "nodev") == 0) 502 options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV; 503 else if (strcasecmp(p, "caseinsensitive") == 0) 504 options[VOLOPT_FLAGS].i_value |= AFPVOL_CASEINSEN; 505 else if (strcasecmp(p, "illegalseq") == 0) 506 options[VOLOPT_FLAGS].i_value |= AFPVOL_EILSEQ; 507 else if (strcasecmp(p, "nocnidcache") == 0) 508 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_CACHE; 509 else if (strcasecmp(p, "tm") == 0) 510 options[VOLOPT_FLAGS].i_value |= AFPVOL_TM; 511 else if (strcasecmp(p, "searchdb") == 0) 512 options[VOLOPT_FLAGS].i_value |= AFPVOL_SEARCHDB; 513 else if (strcasecmp(p, "nonetids") == 0) 514 options[VOLOPT_FLAGS].i_value |= AFPVOL_NONETIDS; 515 else if (strcasecmp(p, "noacls") == 0) 516 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS; 517 else if (strcasecmp(p, "followsymlinks") == 0) 518 options[VOLOPT_FLAGS].i_value |= AFPVOL_FOLLOWSYM; 519 p = strtok(NULL, ","); 520 } 521 522 options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV; 523 } else if (optionok(tmp, "cnidserver:", val)) { 524 setoption(options, save, VOLOPT_CNIDSERVER, val); 525 526 char *p = strrchr(val + 1, ':'); 527 if (p) { 528 *p = 0; 529 setoption(options, save, VOLOPT_CNIDPORT, p); 530 } 531 532 LOG(log_debug, logtype_afpd, "CNID Server for volume '%s': %s:%s", 533 volname, val + 1, p ? p + 1 : Cnid_port); 534 535 } else if (optionok(tmp, "dbpath:", val)) { 536 setoption(options, save, VOLOPT_DBPATH, val); 537 538 } else if (optionok(tmp, "umask:", val)) { 539 options[VOLOPT_UMASK].i_value = (int)strtol(val +1, NULL, 8); 540 } else if (optionok(tmp, "dperm:", val)) { 541 options[VOLOPT_DPERM].i_value = (int)strtol(val+1, NULL, 8); 542 } else if (optionok(tmp, "fperm:", val)) { 543 options[VOLOPT_FPERM].i_value = (int)strtol(val+1, NULL, 8); 544 } else if (optionok(tmp, "perm:", val)) { 545 options[VOLOPT_DFLTPERM].i_value = (int)strtol(val+1, NULL, 8); 546 } else if (optionok(tmp, "password:", val)) { 547 setoption(options, save, VOLOPT_PASSWORD, val); 548 549#ifdef FORCE_UIDGID 550 551 /* this code allows forced uid/gid per volume settings */ 552 } else if (optionok(tmp, "forceuid:", val)) { 553 setoption(options, save, VOLOPT_FORCEUID, val); 554 } else if (optionok(tmp, "forcegid:", val)) { 555 setoption(options, save, VOLOPT_FORCEGID, val); 556 557#endif /* FORCE_UIDGID */ 558 } else if (optionok(tmp, "root_preexec:", val)) { 559 setoption(options, save, VOLOPT_ROOTPREEXEC, val); 560 561 } else if (optionok(tmp, "preexec:", val)) { 562 setoption(options, save, VOLOPT_PREEXEC, val); 563 564 } else if (optionok(tmp, "root_postexec:", val)) { 565 setoption(options, save, VOLOPT_ROOTPOSTEXEC, val); 566 567 } else if (optionok(tmp, "postexec:", val)) { 568 setoption(options, save, VOLOPT_POSTEXEC, val); 569 570 } else if (optionok(tmp, "allowed_hosts:", val)) { 571 setoption(options, save, VOLOPT_ALLOWED_HOSTS, val); 572 573 } else if (optionok(tmp, "denied_hosts:", val)) { 574 setoption(options, save, VOLOPT_DENIED_HOSTS, val); 575 576 } else if (optionok(tmp, "ea:", val)) { 577 if (strcasecmp(val + 1, "ad") == 0) 578 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AD; 579 else if (strcasecmp(val + 1, "sys") == 0) 580 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_SYS; 581 else if (strcasecmp(val + 1, "none") == 0) 582 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_NONE; 583 584 } else if (optionok(tmp, "volsizelimit:", val)) { 585 options[VOLOPT_LIMITSIZE].i_value = (uint32_t)strtoul(val + 1, NULL, 10); 586 587 } else { 588 /* ignore unknown options */ 589 LOG(log_debug, logtype_afpd, "ignoring unknown volume option: %s", tmp); 590 591 } 592} 593 594/* ----------------- */ 595static void showvol(const ucs2_t *name) 596{ 597 struct vol *volume; 598 for ( volume = Volumes; volume; volume = volume->v_next ) { 599 if (volume->v_hide && !strcasecmp_w( volume->v_name, name ) ) { 600 volume->v_hide = 0; 601 return; 602 } 603 } 604} 605 606/* ------------------------------- */ 607static int creatvol(AFPObj *obj, struct passwd *pwd, 608 char *path, char *name, 609 struct vol_option *options, 610 const int user /* user defined volume */ 611 ) 612{ 613 struct vol *volume; 614 int suffixlen, vlen, tmpvlen, u8mvlen, macvlen; 615 int hide = 0; 616 char tmpname[AFPVOL_U8MNAMELEN+1]; 617 ucs2_t u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2]; 618 char suffix[6]; /* max is #FFFF */ 619 u_int16_t flags; 620 621 LOG(log_debug, logtype_afpd, "createvol: Volume '%s'", name); 622 623 if ( name == NULL || *name == '\0' ) { 624 if ((name = strrchr( path, '/' )) == NULL) { 625 return -1; /* Obviously not a fully qualified path */ 626 } 627 628 /* if you wish to share /, you need to specify a name. */ 629 if (*++name == '\0') 630 return -1; 631 } 632 633 /* suffix for mangling use (lastvid + 1) */ 634 /* because v_vid has not been decided yet. */ 635 suffixlen = sprintf(suffix, "%c%X", MANGLE_CHAR, lastvid + 1 ); 636 637 vlen = strlen( name ); 638 639 /* Unicode Volume Name */ 640 /* Firstly convert name from unixcharset to UTF8-MAC */ 641 flags = CONV_IGNORE; 642 tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags); 643 if (tmpvlen <= 0) { 644 strcpy(tmpname, "???"); 645 tmpvlen = 3; 646 } 647 648 /* Do we have to mangle ? */ 649 if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) { 650 if (tmpvlen + suffixlen > obj->options.volnamelen) { 651 flags = CONV_FORCE; 652 tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags); 653 tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0; 654 } 655 strcat(tmpname, suffix); 656 tmpvlen = strlen(tmpname); 657 } 658 659 /* Secondly convert name from UTF8-MAC to UCS2 */ 660 if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) ) 661 return -1; 662 663 LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname); 664 665 /* Maccharset Volume Name */ 666 /* Firsty convert name from unixcharset to maccharset */ 667 flags = CONV_IGNORE; 668 tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags); 669 if (tmpvlen <= 0) { 670 strcpy(tmpname, "???"); 671 tmpvlen = 3; 672 } 673 674 /* Do we have to mangle ? */ 675 if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) { 676 if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) { 677 flags = CONV_FORCE; 678 tmpvlen = convert_charset(obj->options.unixcharset, 679 obj->options.maccharset, 680 0, 681 name, 682 vlen, 683 tmpname, 684 AFPVOL_MACNAMELEN - suffixlen, 685 &flags); 686 tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0; 687 } 688 strcat(tmpname, suffix); 689 tmpvlen = strlen(tmpname); 690 } 691 692 /* Secondly convert name from maccharset to UCS2 */ 693 if ( 0 >= ( macvlen = convert_string(obj->options.maccharset, 694 CH_UCS2, 695 tmpname, 696 tmpvlen, 697 mactmpname, 698 AFPVOL_U8MNAMELEN*2)) ) 699 return -1; 700 701 LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> Longname: '%s'", name, tmpname); 702 703 /* check duplicate */ 704 for ( volume = Volumes; volume; volume = volume->v_next ) { 705 if ((utf8_encoding() && (strcasecmp_w(volume->v_u8mname, u8mtmpname) == 0)) 706 || 707 (!utf8_encoding() && (strcasecmp_w(volume->v_macname, mactmpname) == 0))) { 708 if (volume->v_deleted) { 709 volume->v_new = hide = 1; 710 } else { 711 LOG (log_error, logtype_afpd, 712 "Duplicate volume name, check AppleVolumes files: previous: \"%s\", new: \"%s\"", 713 volume->v_localname, name); 714 return -1; /* Won't be able to access it, anyway... */ 715 } 716 } 717 } 718 719 if (!( volume = (struct vol *)calloc(1, sizeof( struct vol ))) ) { 720 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) ); 721 return -1; 722 } 723 if ( NULL == ( volume->v_localname = strdup(name))) { 724 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) ); 725 free(volume); 726 return -1; 727 } 728 729 if ( NULL == ( volume->v_u8mname = strdup_w(u8mtmpname))) { 730 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) ); 731 volume_free(volume); 732 free(volume); 733 return -1; 734 } 735 if ( NULL == ( volume->v_macname = strdup_w(mactmpname))) { 736 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) ); 737 volume_free(volume); 738 free(volume); 739 return -1; 740 } 741 if (!( volume->v_path = (char *)malloc( strlen( path ) + 1 )) ) { 742 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) ); 743 volume_free(volume); 744 free(volume); 745 return -1; 746 } 747 748 volume->v_name = utf8_encoding()?volume->v_u8mname:volume->v_macname; 749 volume->v_hide = hide; 750 strcpy( volume->v_path, path ); 751 752#ifdef __svr4__ 753 volume->v_qfd = -1; 754#endif /* __svr4__ */ 755 /* os X start at 1 and use network order ie. 1 2 3 */ 756 volume->v_vid = ++lastvid; 757 volume->v_vid = htons(volume->v_vid); 758#ifdef HAVE_ACLS 759 if (!check_vol_acl_support(volume)) { 760 LOG(log_debug, logtype_afpd, "creatvol(\"%s\"): disabling ACL support", volume->v_path); 761 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS; 762 } 763#endif 764 765 /* handle options */ 766 if (options) { 767 volume->v_casefold = options[VOLOPT_CASEFOLD].i_value; 768 volume->v_flags |= options[VOLOPT_FLAGS].i_value; 769 770 if (options[VOLOPT_EA_VFS].i_value) 771 volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value; 772 773 volume->v_ad_options = 0; 774 if ((volume->v_flags & AFPVOL_NODEV)) 775 volume->v_ad_options |= ADVOL_NODEV; 776 if ((volume->v_flags & AFPVOL_CACHE)) 777 volume->v_ad_options |= ADVOL_CACHE; 778 if ((volume->v_flags & AFPVOL_UNIX_PRIV)) 779 volume->v_ad_options |= ADVOL_UNIXPRIV; 780 if ((volume->v_flags & AFPVOL_INV_DOTS)) 781 volume->v_ad_options |= ADVOL_INVDOTS; 782 if ((volume->v_flags & AFPVOL_NOADOUBLE)) 783 volume->v_ad_options |= ADVOL_NOADOUBLE; 784 if ((volume->v_flags & AFPVOL_FOLLOWSYM)) 785 volume->v_ad_options |= ADVOL_FOLLO_SYML; 786 787 if (options[VOLOPT_PASSWORD].c_value) 788 volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value); 789 790 if (options[VOLOPT_VETO].c_value) 791 volume->v_veto = strdup(options[VOLOPT_VETO].c_value); 792 793 if (options[VOLOPT_ENCODING].c_value) 794 volume->v_volcodepage = strdup(options[VOLOPT_ENCODING].c_value); 795 796 if (options[VOLOPT_MACCHARSET].c_value) 797 volume->v_maccodepage = strdup(options[VOLOPT_MACCHARSET].c_value); 798 799 if (options[VOLOPT_DBPATH].c_value) 800 volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_DBPATH].c_value, pwd, path, name); 801 802 if (options[VOLOPT_CNIDSCHEME].c_value) 803 volume->v_cnidscheme = strdup(options[VOLOPT_CNIDSCHEME].c_value); 804 805 if (options[VOLOPT_CNIDSERVER].c_value) 806 volume->v_cnidserver = strdup(options[VOLOPT_CNIDSERVER].c_value); 807 808 if (options[VOLOPT_CNIDPORT].c_value) 809 volume->v_cnidport = strdup(options[VOLOPT_CNIDPORT].c_value); 810 811 if (options[VOLOPT_UMASK].i_value) 812 volume->v_umask = (mode_t)options[VOLOPT_UMASK].i_value; 813 814 if (options[VOLOPT_DPERM].i_value) 815 volume->v_dperm = (mode_t)options[VOLOPT_DPERM].i_value; 816 817 if (options[VOLOPT_FPERM].i_value) 818 volume->v_fperm = (mode_t)options[VOLOPT_FPERM].i_value; 819 820 if (options[VOLOPT_DFLTPERM].i_value) 821 volume->v_perm = (mode_t)options[VOLOPT_DFLTPERM].i_value; 822 823 if (options[VOLOPT_ADOUBLE].i_value) 824 volume->v_adouble = options[VOLOPT_ADOUBLE].i_value; 825 else 826 volume->v_adouble = AD_VERSION; 827 828 if (options[VOLOPT_LIMITSIZE].i_value) 829 volume->v_limitsize = options[VOLOPT_LIMITSIZE].i_value; 830 831 /* Mac to Unix conversion flags*/ 832 volume->v_mtou_flags = 0; 833 if (!(volume->v_flags & AFPVOL_NOHEX)) 834 volume->v_mtou_flags |= CONV_ESCAPEHEX; 835 if (!(volume->v_flags & AFPVOL_USEDOTS)) 836 volume->v_mtou_flags |= CONV_ESCAPEDOTS; 837 if ((volume->v_flags & AFPVOL_EILSEQ)) 838 volume->v_mtou_flags |= CONV__EILSEQ; 839 840 if ((volume->v_casefold & AFPVOL_MTOUUPPER)) 841 volume->v_mtou_flags |= CONV_TOUPPER; 842 else if ((volume->v_casefold & AFPVOL_MTOULOWER)) 843 volume->v_mtou_flags |= CONV_TOLOWER; 844 845 /* Unix to Mac conversion flags*/ 846 volume->v_utom_flags = CONV_IGNORE | CONV_UNESCAPEHEX; 847 if ((volume->v_casefold & AFPVOL_UTOMUPPER)) 848 volume->v_utom_flags |= CONV_TOUPPER; 849 else if ((volume->v_casefold & AFPVOL_UTOMLOWER)) 850 volume->v_utom_flags |= CONV_TOLOWER; 851 852 if ((volume->v_flags & AFPVOL_EILSEQ)) 853 volume->v_utom_flags |= CONV__EILSEQ; 854 855#ifdef FORCE_UIDGID 856 if (options[VOLOPT_FORCEUID].c_value) { 857 volume->v_forceuid = strdup(options[VOLOPT_FORCEUID].c_value); 858 } else { 859 volume->v_forceuid = NULL; /* set as null so as to return 0 later on */ 860 } 861 862 if (options[VOLOPT_FORCEGID].c_value) { 863 volume->v_forcegid = strdup(options[VOLOPT_FORCEGID].c_value); 864 } else { 865 volume->v_forcegid = NULL; /* set as null so as to return 0 later on */ 866 } 867#endif 868 if (!user) { 869 if (options[VOLOPT_PREEXEC].c_value) 870 volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_PREEXEC].c_value, pwd, path, name); 871 volume->v_preexec_close = options[VOLOPT_PREEXEC].i_value; 872 873 if (options[VOLOPT_POSTEXEC].c_value) 874 volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_POSTEXEC].c_value, pwd, path, name); 875 876 if (options[VOLOPT_ROOTPREEXEC].c_value) 877 volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPREEXEC].c_value, pwd, path, name); 878 volume->v_root_preexec_close = options[VOLOPT_ROOTPREEXEC].i_value; 879 880 if (options[VOLOPT_ROOTPOSTEXEC].c_value) 881 volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPOSTEXEC].c_value, pwd, path, name); 882 } 883 } 884 volume->v_dperm |= volume->v_perm; 885 volume->v_fperm |= volume->v_perm; 886 887 /* Check EA support on volume */ 888 if (volume->v_vfs_ea == AFPVOL_EA_AUTO) 889 check_ea_sys_support(volume); 890 initvol_vfs(volume); 891 892 /* get/store uuid from file in afpd master*/ 893 if ((parent_or_child == 0) && (volume->v_flags & AFPVOL_TM)) { 894 char *uuid = get_vol_uuid(obj, volume->v_localname); 895 if (!uuid) { 896 LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID", 897 volume->v_localname); 898 } else { 899 volume->v_uuid = uuid; 900 LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'", 901 volume->v_localname, volume->v_uuid); 902 } 903 } 904 905 volume->v_next = Volumes; 906 Volumes = volume; 907 return 0; 908} 909 910/* ---------------- */ 911static char *myfgets( char *buf, int size, FILE *fp) 912{ 913 char *p; 914 int c; 915 916 p = buf; 917 while ((EOF != ( c = getc( fp )) ) && ( size > 1 )) { 918 if ( c == '\n' || c == '\r' ) { 919 if (p != buf && *(p -1) == '\\') { 920 p--; 921 size++; 922 continue; 923 } 924 *p++ = '\n'; 925 break; 926 } else { 927 *p++ = c; 928 } 929 size--; 930 } 931 932 if ( p == buf ) { 933 return( NULL ); 934 } else { 935 *p = '\0'; 936 return( buf ); 937 } 938} 939 940 941/* check access list. this function wants something of the following 942 * form: 943 * @group,name,name2,@group2,name3 944 * 945 * a NULL argument allows everybody to have access. 946 * we return three things: 947 * -1: no list 948 * 0: list exists, but name isn't in it 949 * 1: in list 950 */ 951 952#ifndef NO_REAL_USER_NAME 953/* authentication is case insensitive 954 * FIXME should we do the same with group name? 955 */ 956#define access_strcmp strcasecmp 957 958#else 959#define access_strcmp strcmp 960 961#endif 962 963static int accessvol(const char *args, const char *name) 964{ 965 char buf[MAXPATHLEN + 1], *p; 966 struct group *gr; 967 968 if (!args) 969 return -1; 970 971 strlcpy(buf, args, sizeof(buf)); 972 if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */ 973 return -1; 974 975 while (p) { 976 if (*p == '@') { /* it's a group */ 977 if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid)) 978 return 1; 979 } else if (access_strcmp(p, name) == 0) /* it's a user name */ 980 return 1; 981 p = strtok(NULL, ","); 982 } 983 984 return 0; 985} 986 987static int hostaccessvol(int type, const char *volname, const char *args, const AFPObj *obj) 988{ 989 int mask_int; 990 char buf[MAXPATHLEN + 1], *p, *b; 991 DSI *dsi = obj->handle; 992 struct sockaddr_storage client; 993 994 if (!args) 995 return -1; 996 997 strlcpy(buf, args, sizeof(buf)); 998 if ((p = strtok_r(buf, ",", &b)) == NULL) /* nothing, return okay */ 999 return -1; 1000 1001 if (obj->proto != AFPPROTO_DSI) 1002 return -1; 1003 1004 while (p) { 1005 int ret; 1006 char *ipaddr, *mask_char; 1007 struct addrinfo hints, *ai; 1008 1009 ipaddr = strtok(p, "/"); 1010 mask_char = strtok(NULL,"/"); 1011 1012 /* Get address from string with getaddrinfo */ 1013 memset(&hints, 0, sizeof hints); 1014 hints.ai_family = AF_UNSPEC; 1015 hints.ai_socktype = SOCK_STREAM; 1016 if ((ret = getaddrinfo(ipaddr, NULL, &hints, &ai)) != 0) { 1017 LOG(log_error, logtype_afpd, "hostaccessvol: getaddrinfo: %s\n", gai_strerror(ret)); 1018 continue; 1019 } 1020 1021 /* netmask */ 1022 if (mask_char != NULL) 1023 mask_int = atoi(mask_char); /* apply_ip_mask does range checking on it */ 1024 else { 1025 if (ai->ai_family == AF_INET) /* IPv4 */ 1026 mask_int = 32; 1027 else /* IPv6 */ 1028 mask_int = 128; 1029 } 1030 1031 /* Apply mask to addresses */ 1032 client = dsi->client; 1033 apply_ip_mask((struct sockaddr *)&client, mask_int); 1034 apply_ip_mask(ai->ai_addr, mask_int); 1035 1036 if (compare_ip((struct sockaddr *)&client, ai->ai_addr) == 0) { 1037 if (type == VOLOPT_DENIED_HOSTS) 1038 LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s' by denied list", 1039 getip_string((struct sockaddr *)&client), volname); 1040 freeaddrinfo(ai); 1041 return 1; 1042 } 1043 1044 /* next address */ 1045 freeaddrinfo(ai); 1046 p = strtok_r(NULL, ",", &b); 1047 } 1048 1049 if (type == VOLOPT_ALLOWED_HOSTS) 1050 LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s', not in allowed list", 1051 getip_string((struct sockaddr *)&dsi->client), volname); 1052 return 0; 1053} 1054 1055static void setextmap(char *ext, char *type, char *creator, int user) 1056{ 1057 struct extmap *em; 1058 int cnt; 1059 1060 if (Extmap == NULL) { 1061 if (( Extmap = calloc(1, sizeof( struct extmap ))) == NULL ) { 1062 LOG(log_error, logtype_afpd, "setextmap: calloc: %s", strerror(errno) ); 1063 return; 1064 } 1065 } 1066 ext++; 1067 for ( em = Extmap, cnt = 0; em->em_ext; em++, cnt++) { 1068 if ( (strdiacasecmp( em->em_ext, ext )) == 0 ) { 1069 break; 1070 } 1071 } 1072 1073 if ( em->em_ext == NULL ) { 1074 if (!(Extmap = realloc( Extmap, sizeof( struct extmap ) * (cnt +2))) ) { 1075 LOG(log_error, logtype_afpd, "setextmap: realloc: %s", strerror(errno) ); 1076 return; 1077 } 1078 (Extmap +cnt +1)->em_ext = NULL; 1079 em = Extmap +cnt; 1080 } else if ( !user ) { 1081 return; 1082 } 1083 if (em->em_ext) 1084 free(em->em_ext); 1085 1086 if (!(em->em_ext = strdup( ext))) { 1087 LOG(log_error, logtype_afpd, "setextmap: strdup: %s", strerror(errno) ); 1088 return; 1089 } 1090 1091 if ( *type == '\0' ) { 1092 memcpy(em->em_type, "\0\0\0\0", sizeof( em->em_type )); 1093 } else { 1094 memcpy(em->em_type, type, sizeof( em->em_type )); 1095 } 1096 if ( *creator == '\0' ) { 1097 memcpy(em->em_creator, "\0\0\0\0", sizeof( em->em_creator )); 1098 } else { 1099 memcpy(em->em_creator, creator, sizeof( em->em_creator )); 1100 } 1101} 1102 1103/* -------------------------- */ 1104static int extmap_cmp(const void *map1, const void *map2) 1105{ 1106 const struct extmap *em1 = map1; 1107 const struct extmap *em2 = map2; 1108 return strdiacasecmp(em1->em_ext, em2->em_ext); 1109} 1110 1111static void sortextmap( void) 1112{ 1113 struct extmap *em; 1114 1115 Extmap_cnt = 0; 1116 if ((em = Extmap) == NULL) { 1117 return; 1118 } 1119 while (em->em_ext) { 1120 em++; 1121 Extmap_cnt++; 1122 } 1123 if (Extmap_cnt) { 1124 qsort(Extmap, Extmap_cnt, sizeof(struct extmap), extmap_cmp); 1125 if (*Extmap->em_ext == 0) { 1126 /* the first line is really "." the default entry, 1127 * we remove the leading '.' in setextmap 1128 */ 1129 Defextmap = Extmap; 1130 } 1131 } 1132} 1133 1134/* ---------------------- 1135 */ 1136static void free_extmap( void) 1137{ 1138 struct extmap *em; 1139 1140 if (Extmap) { 1141 for ( em = Extmap; em->em_ext; em++) { 1142 free (em->em_ext); 1143 } 1144 free(Extmap); 1145 Extmap = NULL; 1146 Defextmap = Extmap; 1147 Extmap_cnt = 0; 1148 } 1149} 1150 1151/* ---------------------- 1152 */ 1153static int volfile_changed(struct afp_volume_name *p) 1154{ 1155 struct stat st; 1156 char *name; 1157 1158 if (p->full_name) 1159 name = p->full_name; 1160 else 1161 name = p->name; 1162 1163 if (!stat( name, &st) && st.st_mtime > p->mtime) { 1164 p->mtime = st.st_mtime; 1165 return 1; 1166 } 1167 return 0; 1168} 1169 1170/* ---------------------- 1171 * Read a volume configuration file and add the volumes contained within to 1172 * the global volume list. This gets called from the forked afpd childs. 1173 * The master now reads this too for Zeroconf announcements. 1174 * 1175 * If p2 is non-NULL, the file that is opened is 1176 * p1/p2 1177 * 1178 * Lines that begin with # and blank lines are ignored. 1179 * Volume lines are of the form: 1180 * <unix path> [<volume name>] [allow:<user>,<@group>,...] \ 1181 * [codepage:<file>] [casefold:<num>] 1182 * <extension> TYPE [CREATOR] 1183 * 1184 */ 1185static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent) 1186{ 1187 FILE *fp; 1188 char path[MAXPATHLEN + 1]; 1189 char tmp[MAXPATHLEN + 1]; 1190 char volname[AFPVOL_U8MNAMELEN + 1]; 1191 char buf[BUFSIZ]; 1192 char type[5], creator[5]; 1193 char *u, *p; 1194 int fd; 1195 int i; 1196 struct passwd *pw; 1197 struct vol_option save_options[VOLOPT_NUM]; 1198 struct vol_option default_options[VOLOPT_NUM]; 1199 struct vol_option options[VOLOPT_NUM]; 1200 struct stat st; 1201 1202 if (!p1->name) 1203 return -1; 1204 p1->mtime = 0; 1205 strcpy( path, p1->name ); 1206 if ( p2 != NULL ) { 1207 strcat( path, "/" ); 1208 strcat( path, p2 ); 1209 if (p1->full_name) { 1210 free(p1->full_name); 1211 } 1212 p1->full_name = strdup(path); 1213 } 1214 1215 if (NULL == ( fp = fopen( path, "r" )) ) { 1216 return( -1 ); 1217 } 1218 fd = fileno(fp); 1219 if (fd != -1 && !fstat( fd, &st) ) { 1220 p1->mtime = st.st_mtime; 1221 } 1222 1223 /* try putting a read lock on the volume file twice, sleep 1 second if first attempt fails */ 1224 int retries = 2; 1225 while (1) { 1226 if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) { 1227 retries--; 1228 if (!retries) { 1229 LOG(log_error, logtype_afpd, "readvolfile: can't lock volume file \"%s\"", path); 1230 if ( fclose( fp ) != 0 ) { 1231 LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) ); 1232 } 1233 return -1; 1234 } 1235 sleep(1); 1236 continue; 1237 } 1238 break; 1239 } 1240 1241 memset(default_options, 0, sizeof(default_options)); 1242 1243 /* Enable some default options for all volumes */ 1244 default_options[VOLOPT_FLAGS].i_value |= AFPVOL_CACHE; 1245#ifdef HAVE_ACLS 1246 default_options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS; 1247#endif 1248 default_options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AUTO; 1249 LOG(log_maxdebug, logtype_afpd, "readvolfile: seeding default umask: %04o", 1250 obj->options.umask); 1251 default_options[VOLOPT_UMASK].i_value = obj->options.umask; 1252 memcpy(save_options, default_options, sizeof(options)); 1253 1254 LOG(log_debug, logtype_afpd, "readvolfile: \"%s\"", path); 1255 1256 while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) { 1257 initline( strlen( buf ), buf ); 1258 parseline( sizeof( path ) - 1, path ); 1259 switch ( *path ) { 1260 case '\0' : 1261 case '#' : 1262 continue; 1263 1264 case ':': 1265 /* change the default options for this file */ 1266 if (strncmp(path, VOLOPT_DEFAULT, VOLOPT_DEFAULT_LEN) == 0) { 1267 volfree(default_options, save_options); 1268 memcpy(default_options, save_options, sizeof(options)); 1269 *tmp = '\0'; 1270 for (i = 0; i < VOLOPT_NUM; i++) { 1271 if (parseline( sizeof( path ) - VOLOPT_DEFAULT_LEN - 1, 1272 path + VOLOPT_DEFAULT_LEN) < 0) 1273 break; 1274 volset(default_options, NULL, tmp, sizeof(tmp) - 1, 1275 path + VOLOPT_DEFAULT_LEN); 1276 } 1277 } 1278 break; 1279 1280 case '~' : 1281 if (( p = strchr( path, '/' )) != NULL ) { 1282 *p++ = '\0'; 1283 } 1284 u = path; 1285 u++; 1286 if ( *u == '\0' ) { 1287 u = obj->username; 1288 } 1289 if ( u == NULL || *u == '\0' || ( pw = getpwnam( u )) == NULL ) { 1290 continue; 1291 } 1292 strcpy( tmp, pw->pw_dir ); 1293 if ( p != NULL && *p != '\0' ) { 1294 strcat( tmp, "/" ); 1295 strcat( tmp, p ); 1296 } 1297 /* fall through */ 1298 1299 case '/' : 1300 /* send path through variable substitution */ 1301 if (*path != '~') /* need to copy path to tmp */ 1302 strcpy(tmp, path); 1303 if (!pwent && obj->username) 1304 pwent = getpwnam(obj->username); 1305 1306 if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL) 1307 continue; 1308 1309 /* this is sort of braindead. basically, i want to be 1310 * able to specify things in any order, but i don't want to 1311 * re-write everything. */ 1312 1313 memcpy(options, default_options, sizeof(options)); 1314 *volname = '\0'; 1315 1316 /* read in up to VOLOP_NUM possible options */ 1317 for (i = 0; i < VOLOPT_NUM; i++) { 1318 if (parseline( sizeof( tmp ) - 1, tmp ) < 0) 1319 break; 1320 1321 volset(options, default_options, volname, sizeof(volname) - 1, tmp); 1322 } 1323 1324 /* check allow/deny lists (if not afpd master loading volumes for Zeroconf reg.): 1325 allow -> either no list (-1), or in list (1) 1326 deny -> either no list (-1), or not in list (0) */ 1327 if (parent_or_child == 0 1328 || 1329 (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) && 1330 (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) && 1331 hostaccessvol(VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value, obj) && 1332 (hostaccessvol(VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value, obj) < 1))) { 1333 1334 /* handle read-only behaviour. semantics: 1335 * 1) neither the rolist nor the rwlist exist -> rw 1336 * 2) rolist exists -> ro if user is in it. 1337 * 3) rwlist exists -> ro unless user is in it. */ 1338 if (parent_or_child == 1 1339 && 1340 ((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0) 1341 && 1342 ((accessvol(options[VOLOPT_ROLIST].c_value, obj->username) == 1) || 1343 !accessvol(options[VOLOPT_RWLIST].c_value, obj->username))) 1344 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO; 1345 1346 /* do variable substitution for volname */ 1347 if (volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL) == NULL) 1348 continue; 1349 1350 creatvol(obj, pwent, path, tmp, options, p2 != NULL); 1351 } 1352 volfree(options, default_options); 1353 break; 1354 1355 case '.' : 1356 parseline( sizeof( type ) - 1, type ); 1357 parseline( sizeof( creator ) - 1, creator ); 1358 setextmap( path, type, creator, user); 1359 break; 1360 1361 default : 1362 break; 1363 } 1364 } 1365 volfree(save_options, NULL); 1366 sortextmap(); 1367 if ( fclose( fp ) != 0 ) { 1368 LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) ); 1369 } 1370 p1->loaded = 1; 1371 return( 0 ); 1372} 1373 1374/* ------------------------------- */ 1375static void volume_free(struct vol *vol) 1376{ 1377 free(vol->v_localname); 1378 vol->v_localname = NULL; 1379 free(vol->v_u8mname); 1380 vol->v_u8mname = NULL; 1381 free(vol->v_macname); 1382 vol->v_macname = NULL; 1383 free(vol->v_path); 1384 free(vol->v_password); 1385 free(vol->v_veto); 1386 free(vol->v_volcodepage); 1387 free(vol->v_maccodepage); 1388 free(vol->v_cnidscheme); 1389 free(vol->v_dbpath); 1390 free(vol->v_gvs); 1391#ifdef FORCE_UIDGID 1392 free(vol->v_forceuid); 1393 free(vol->v_forcegid); 1394#endif /* FORCE_UIDGID */ 1395 if (vol->v_uuid) 1396 free(vol->v_uuid); 1397} 1398 1399/* ------------------------------- */ 1400static void free_volumes(void ) 1401{ 1402 struct vol *vol; 1403 struct vol *nvol, *ovol; 1404 1405 for ( vol = Volumes; vol; vol = vol->v_next ) { 1406 if (( vol->v_flags & AFPVOL_OPEN ) ) { 1407 vol->v_deleted = 1; 1408 continue; 1409 } 1410 volume_free(vol); 1411 } 1412 1413 for ( vol = Volumes, ovol = NULL; vol; vol = nvol) { 1414 nvol = vol->v_next; 1415 1416 if (vol->v_localname == NULL) { 1417 if (Volumes == vol) { 1418 Volumes = nvol; 1419 ovol = Volumes; 1420 } 1421 else { 1422 ovol->v_next = nvol; 1423 } 1424 free(vol); 1425 } 1426 else { 1427 ovol = vol; 1428 } 1429 } 1430} 1431 1432/* ------------------------------- */ 1433static void volume_unlink(struct vol *volume) 1434{ 1435 struct vol *vol, *ovol, *nvol; 1436 1437 if (volume == Volumes) { 1438 Volumes = Volumes->v_next; 1439 return; 1440 } 1441 for ( vol = Volumes->v_next, ovol = Volumes; vol; vol = nvol) { 1442 nvol = vol->v_next; 1443 1444 if (vol == volume) { 1445 ovol->v_next = nvol; 1446 break; 1447 } 1448 else { 1449 ovol = vol; 1450 } 1451 } 1452} 1453/*! 1454 * Read band-size info from Info.plist XML file of an TM sparsebundle 1455 * 1456 * @param path (r) path to Info.plist file 1457 * @return band-size in bytes, -1 on error 1458 */ 1459static long long int get_tm_bandsize(const char *path) 1460{ 1461 EC_INIT; 1462 FILE *file = NULL; 1463 char buf[512]; 1464 long long int bandsize = -1; 1465 1466 EC_NULL_LOGSTR( file = fopen(path, "r"), 1467 "get_tm_bandsize(\"%s\"): %s", 1468 path, strerror(errno) ); 1469 1470 while (fgets(buf, sizeof(buf), file) != NULL) { 1471 if (strstr(buf, "band-size") == NULL) 1472 continue; 1473 1474 if (fscanf(file, " <integer>%lld</integer>", &bandsize) != 1) { 1475 LOG(log_error, logtype_afpd, "get_tm_bandsize(\"%s\"): can't parse band-size", path); 1476 EC_FAIL; 1477 } 1478 break; 1479 } 1480 1481EC_CLEANUP: 1482 if (file) 1483 fclose(file); 1484 LOG(log_debug, logtype_afpd, "get_tm_bandsize(\"%s\"): bandsize: %lld", path, bandsize); 1485 return bandsize; 1486} 1487 1488/*! 1489 * Return number on entries in a directory 1490 * 1491 * @param path (r) path to dir 1492 * @return number of entries, -1 on error 1493 */ 1494static long long int get_tm_bands(const char *path) 1495{ 1496 EC_INIT; 1497 long long int count = 0; 1498 DIR *dir = NULL; 1499 const struct dirent *entry; 1500 1501 EC_NULL( dir = opendir(path) ); 1502 1503 while ((entry = readdir(dir)) != NULL) 1504 count++; 1505 count -= 2; /* All OSens I'm aware of return "." and "..", so just substract them, avoiding string comparison in loop */ 1506 1507EC_CLEANUP: 1508 if (dir) 1509 closedir(dir); 1510 if (ret != 0) 1511 return -1; 1512 return count; 1513} 1514 1515/*! 1516 * Calculate used size of a TimeMachine volume 1517 * 1518 * This assumes that the volume is used only for TimeMachine. 1519 * 1520 * 1) readdir(path of volume) 1521 * 2) for every element that matches regex "\(.*\)\.sparsebundle$" : 1522 * 3) parse "\1.sparsebundle/Info.plist" and read the band-size XML key integer value 1523 * 4) readdir "\1.sparsebundle/bands/" counting files 1524 * 5) calculate used size as: (file_count - 1) * band-size 1525 * 1526 * The result of the calculation is returned in "volume->v_tm_used". 1527 * "volume->v_appended" gets reset to 0. 1528 * "volume->v_tm_cachetime" is updated with the current time from time(NULL). 1529 * 1530 * "volume->v_tm_used" is cached for TM_USED_CACHETIME seconds and updated by 1531 * "volume->v_appended". The latter is increased by X every time the client 1532 * appends X bytes to a file (in fork.c). 1533 * 1534 * @param vol (rw) volume to calculate 1535 * @return 0 on success, -1 on error 1536 */ 1537#define TM_USED_CACHETIME 60 /* cache for 60 seconds */ 1538static int get_tm_used(struct vol * restrict vol) 1539{ 1540 EC_INIT; 1541 long long int bandsize; 1542 VolSpace used = 0; 1543 bstring infoplist = NULL; 1544 bstring bandsdir = NULL; 1545 DIR *dir = NULL; 1546 const struct dirent *entry; 1547 const char *p; 1548 struct stat st; 1549 long int links; 1550 time_t now = time(NULL); 1551 1552 if (vol->v_tm_cachetime 1553 && ((vol->v_tm_cachetime + TM_USED_CACHETIME) >= now)) { 1554 if (vol->v_tm_used == -1) 1555 EC_FAIL; 1556 vol->v_tm_used += vol->v_appended; 1557 vol->v_appended = 0; 1558 LOG(log_debug, logtype_afpd, "getused(\"%s\"): cached: %" PRIu64 " bytes", 1559 vol->v_path, vol->v_tm_used); 1560 return 0; 1561 } 1562 1563 vol->v_tm_cachetime = now; 1564 1565 EC_NULL( dir = opendir(vol->v_path) ); 1566 1567 while ((entry = readdir(dir)) != NULL) { 1568 if (((p = strstr(entry->d_name, "sparsebundle")) != NULL) 1569 && (strlen(entry->d_name) == (p + strlen("sparsebundle") - entry->d_name))) { 1570 1571 EC_NULL_LOG( infoplist = bformat("%s/%s/%s", vol->v_path, entry->d_name, "Info.plist") ); 1572 1573 if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1) 1574 continue; 1575 1576 EC_NULL_LOG( bandsdir = bformat("%s/%s/%s/", vol->v_path, entry->d_name, "bands") ); 1577 1578 if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1) 1579 continue; 1580 1581 used += (links - 1) * bandsize; 1582 LOG(log_debug, logtype_afpd, "getused(\"%s\"): bands: %" PRIu64 " bytes", 1583 cfrombstr(bandsdir), used); 1584 } 1585 } 1586 1587 vol->v_tm_used = used; 1588 1589EC_CLEANUP: 1590 if (infoplist) 1591 bdestroy(infoplist); 1592 if (bandsdir) 1593 bdestroy(bandsdir); 1594 if (dir) 1595 closedir(dir); 1596 1597 LOG(log_debug, logtype_afpd, "getused(\"%s\"): %" PRIu64 " bytes", vol->v_path, vol->v_tm_used); 1598 1599 EC_EXIT; 1600} 1601 1602static int getvolspace(struct vol *vol, 1603 u_int32_t *bfree, u_int32_t *btotal, 1604 VolSpace *xbfree, VolSpace *xbtotal, u_int32_t *bsize) 1605{ 1606 int spaceflag, rc; 1607 u_int32_t maxsize; 1608 VolSpace used; 1609#ifndef NO_QUOTA_SUPPORT 1610 VolSpace qfree, qtotal; 1611#endif 1612 1613 spaceflag = AFPVOL_GVSMASK & vol->v_flags; 1614 /* report up to 2GB if afp version is < 2.2 (4GB if not) */ 1615 maxsize = (vol->v_flags & AFPVOL_A2VOL) ? 0x01fffe00 : 1616 (((afp_version < 22) || (vol->v_flags & AFPVOL_LIMITSIZE)) 1617 ? 0x7fffffffL : 0xffffffffL); 1618 1619#ifdef AFS 1620 if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) { 1621 if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) { 1622 vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS; 1623 goto getvolspace_done; 1624 } 1625 } 1626#endif 1627 1628 if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal, bsize)) != AFP_OK ) { 1629 return( rc ); 1630 } 1631 1632#ifndef NO_QUOTA_SUPPORT 1633 if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) { 1634 if ( uquota_getvolspace( vol, &qfree, &qtotal, *bsize ) == AFP_OK ) { 1635 vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA; 1636 *xbfree = MIN(*xbfree, qfree); 1637 *xbtotal = MIN(*xbtotal, qtotal); 1638 goto getvolspace_done; 1639 } 1640 } 1641#endif 1642 vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS; 1643 1644getvolspace_done: 1645 if (vol->v_limitsize) { 1646 if (get_tm_used(vol) != 0) 1647 return AFPERR_MISC; 1648 1649 *xbtotal = MIN(*xbtotal, (vol->v_limitsize * 1024 * 1024)); 1650 *xbfree = MIN(*xbfree, *xbtotal < vol->v_tm_used ? 0 : *xbtotal - vol->v_tm_used); 1651 1652 LOG(log_debug, logtype_afpd, 1653 "volparams: total: %" PRIu64 ", used: %" PRIu64 ", free: %" PRIu64 " bytes", 1654 *xbtotal, vol->v_tm_used, *xbfree); 1655 } 1656 1657 *bfree = MIN(*xbfree, maxsize); 1658 *btotal = MIN(*xbtotal, maxsize); 1659 return( AFP_OK ); 1660} 1661 1662#define FCE_TM_DELTA 10 /* send notification every 10 seconds */ 1663void vol_fce_tm_event(void) 1664{ 1665 static time_t last; 1666 time_t now = time(NULL); 1667 struct vol *vol = Volumes; 1668 1669 if ((last + FCE_TM_DELTA) < now) { 1670 last = now; 1671 for ( ; vol; vol = vol->v_next ) { 1672 if (vol->v_flags & AFPVOL_TM) 1673 (void)fce_register_tm_size(vol->v_path, vol->v_tm_used + vol->v_appended); 1674 } 1675 } 1676} 1677 1678/* ----------------------- 1679 * set volume creation date 1680 * avoid duplicate, well at least it tries 1681 */ 1682static void vol_setdate(u_int16_t id, struct adouble *adp, time_t date) 1683{ 1684 struct vol *volume; 1685 struct vol *vol = Volumes; 1686 1687 for ( volume = Volumes; volume; volume = volume->v_next ) { 1688 if (volume->v_vid == id) { 1689 vol = volume; 1690 } 1691 else if ((time_t)(AD_DATE_FROM_UNIX(date)) == volume->v_ctime) { 1692 date = date+1; 1693 volume = Volumes; /* restart */ 1694 } 1695 } 1696 vol->v_ctime = AD_DATE_FROM_UNIX(date); 1697 ad_setdate(adp, AD_DATE_CREATE | AD_DATE_UNIX, date); 1698} 1699 1700/* ----------------------- */ 1701static int getvolparams( u_int16_t bitmap, struct vol *vol, struct stat *st, char *buf, size_t *buflen) 1702{ 1703 struct adouble ad; 1704 int bit = 0, isad = 1; 1705 u_int32_t aint; 1706 u_short ashort; 1707 u_int32_t bfree, btotal, bsize; 1708 VolSpace xbfree, xbtotal; /* extended bytes */ 1709 char *data, *nameoff = NULL; 1710 char *slash; 1711 1712 LOG(log_debug, logtype_afpd, "getvolparams: Volume '%s'", vol->v_localname); 1713 1714 /* courtesy of jallison@whistle.com: 1715 * For MacOS8.x support we need to create the 1716 * .Parent file here if it doesn't exist. */ 1717 1718 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 1719 if ( ad_open_metadata( vol->v_path, ADFLAGS_DIR, O_CREAT, &ad) < 0 ) { 1720 isad = 0; 1721 vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime); 1722 1723 } else if (ad_get_MD_flags( &ad ) & O_CREAT) { 1724 slash = strrchr( vol->v_path, '/' ); 1725 if(slash) 1726 slash++; 1727 else 1728 slash = vol->v_path; 1729 if (ad_getentryoff(&ad, ADEID_NAME)) { 1730 ad_setentrylen( &ad, ADEID_NAME, strlen( slash )); 1731 memcpy(ad_entry( &ad, ADEID_NAME ), slash, 1732 ad_getentrylen( &ad, ADEID_NAME )); 1733 } 1734 vol_setdate(vol->v_vid, &ad, st->st_mtime); 1735 ad_flush(&ad); 1736 } 1737 else { 1738 if (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0) 1739 vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime); 1740 else 1741 vol->v_ctime = aint; 1742 } 1743 1744 if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) | 1745 (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) | 1746 (1<<VOLPBIT_BSIZE)) ) != 0 ) { 1747 if ( getvolspace( vol, &bfree, &btotal, &xbfree, &xbtotal, 1748 &bsize) != AFP_OK ) { 1749 if ( isad ) { 1750 ad_close( &ad, ADFLAGS_HF ); 1751 } 1752 return( AFPERR_PARAM ); 1753 } 1754 } 1755 1756 data = buf; 1757 while ( bitmap != 0 ) { 1758 while (( bitmap & 1 ) == 0 ) { 1759 bitmap = bitmap>>1; 1760 bit++; 1761 } 1762 1763 switch ( bit ) { 1764 case VOLPBIT_ATTR : 1765 ashort = 0; 1766 /* check for read-only. 1767 * NOTE: we don't actually set the read-only flag unless 1768 * it's passed in that way as it's possible to mount 1769 * a read-write filesystem under a read-only one. */ 1770 if ((vol->v_flags & AFPVOL_RO) || 1771 ((utime(vol->v_path, NULL) < 0) && (errno == EROFS))) { 1772 ashort |= VOLPBIT_ATTR_RO; 1773 } 1774 /* prior 2.1 only VOLPBIT_ATTR_RO is defined */ 1775 if (afp_version > 20) { 1776 if (0 == (vol->v_flags & AFPVOL_NOFILEID) && vol->v_cdb != NULL && 1777 (vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) { 1778 ashort |= VOLPBIT_ATTR_FILEID; 1779 } 1780 ashort |= VOLPBIT_ATTR_CATSEARCH; 1781 1782 if (afp_version >= 30) { 1783 ashort |= VOLPBIT_ATTR_UTF8; 1784 if (vol->v_flags & AFPVOL_UNIX_PRIV) 1785 ashort |= VOLPBIT_ATTR_UNIXPRIV; 1786 if (vol->v_flags & AFPVOL_TM) 1787 ashort |= VOLPBIT_ATTR_TM; 1788 if (vol->v_flags & AFPVOL_NONETIDS) 1789 ashort |= VOLPBIT_ATTR_NONETIDS; 1790 if (afp_version >= 32) { 1791 if (vol->v_vfs_ea) 1792 ashort |= VOLPBIT_ATTR_EXT_ATTRS; 1793 if (vol->v_flags & AFPVOL_ACLS) 1794 ashort |= VOLPBIT_ATTR_ACLS; 1795 } 1796 } 1797 } 1798 ashort = htons(ashort); 1799 memcpy(data, &ashort, sizeof( ashort )); 1800 data += sizeof( ashort ); 1801 break; 1802 1803 case VOLPBIT_SIG : 1804 ashort = htons( AFPVOLSIG_DEFAULT ); 1805 memcpy(data, &ashort, sizeof( ashort )); 1806 data += sizeof( ashort ); 1807 break; 1808 1809 case VOLPBIT_CDATE : 1810 aint = vol->v_ctime; 1811 memcpy(data, &aint, sizeof( aint )); 1812 data += sizeof( aint ); 1813 break; 1814 1815 case VOLPBIT_MDATE : 1816 if ( st->st_mtime > vol->v_mtime ) { 1817 vol->v_mtime = st->st_mtime; 1818 } 1819 aint = AD_DATE_FROM_UNIX(vol->v_mtime); 1820 memcpy(data, &aint, sizeof( aint )); 1821 data += sizeof( aint ); 1822 break; 1823 1824 case VOLPBIT_BDATE : 1825 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0)) 1826 aint = AD_DATE_START; 1827 memcpy(data, &aint, sizeof( aint )); 1828 data += sizeof( aint ); 1829 break; 1830 1831 case VOLPBIT_VID : 1832 memcpy(data, &vol->v_vid, sizeof( vol->v_vid )); 1833 data += sizeof( vol->v_vid ); 1834 break; 1835 1836 case VOLPBIT_BFREE : 1837 bfree = htonl( bfree ); 1838 memcpy(data, &bfree, sizeof( bfree )); 1839 data += sizeof( bfree ); 1840 break; 1841 1842 case VOLPBIT_BTOTAL : 1843 btotal = htonl( btotal ); 1844 memcpy(data, &btotal, sizeof( btotal )); 1845 data += sizeof( btotal ); 1846 break; 1847 1848#ifndef NO_LARGE_VOL_SUPPORT 1849 case VOLPBIT_XBFREE : 1850 xbfree = hton64( xbfree ); 1851#if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG) 1852 bcopy(&xbfree, data, sizeof(xbfree)); 1853#else /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */ 1854 memcpy(data, &xbfree, sizeof( xbfree )); 1855#endif /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */ 1856 data += sizeof( xbfree ); 1857 break; 1858 1859 case VOLPBIT_XBTOTAL : 1860 xbtotal = hton64( xbtotal ); 1861#if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG) 1862 bcopy(&xbtotal, data, sizeof(xbtotal)); 1863#else /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */ 1864 memcpy(data, &xbtotal, sizeof( xbtotal )); 1865#endif /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */ 1866 data += sizeof( xbfree ); 1867 break; 1868#endif /* ! NO_LARGE_VOL_SUPPORT */ 1869 1870 case VOLPBIT_NAME : 1871 nameoff = data; 1872 data += sizeof( u_int16_t ); 1873 break; 1874 1875 case VOLPBIT_BSIZE: /* block size */ 1876 bsize = htonl(bsize); 1877 memcpy(data, &bsize, sizeof(bsize)); 1878 data += sizeof(bsize); 1879 break; 1880 1881 default : 1882 if ( isad ) { 1883 ad_close( &ad, ADFLAGS_HF ); 1884 } 1885 return( AFPERR_BITMAP ); 1886 } 1887 bitmap = bitmap>>1; 1888 bit++; 1889 } 1890 if ( nameoff ) { 1891 ashort = htons( data - buf ); 1892 memcpy(nameoff, &ashort, sizeof( ashort )); 1893 /* name is always in mac charset */ 1894 aint = ucs2_to_charset( vol->v_maccharset, vol->v_macname, data+1, AFPVOL_MACNAMELEN + 1); 1895 if ( aint <= 0 ) { 1896 *buflen = 0; 1897 return AFPERR_MISC; 1898 } 1899 1900 *data++ = aint; 1901 data += aint; 1902 } 1903 if ( isad ) { 1904 ad_close_metadata( &ad); 1905 } 1906 *buflen = data - buf; 1907 return( AFP_OK ); 1908} 1909 1910/* ------------------------- */ 1911static int stat_vol(u_int16_t bitmap, struct vol *vol, char *rbuf, size_t *rbuflen) 1912{ 1913 struct stat st; 1914 int ret; 1915 size_t buflen; 1916 1917 if ( stat( vol->v_path, &st ) < 0 ) { 1918 *rbuflen = 0; 1919 return( AFPERR_PARAM ); 1920 } 1921 /* save the volume device number */ 1922 vol->v_dev = st.st_dev; 1923 1924 buflen = *rbuflen - sizeof( bitmap ); 1925 if (( ret = getvolparams( bitmap, vol, &st, 1926 rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) { 1927 *rbuflen = 0; 1928 return( ret ); 1929 } 1930 *rbuflen = buflen + sizeof( bitmap ); 1931 bitmap = htons( bitmap ); 1932 memcpy(rbuf, &bitmap, sizeof( bitmap )); 1933 return( AFP_OK ); 1934 1935} 1936 1937/* ------------------------------- */ 1938void load_volumes(AFPObj *obj) 1939{ 1940 int ret; 1941 struct passwd *pwent = NULL; 1942 1943 if (Volumes) { 1944 int changed = 0; 1945 1946 /* check files date */ 1947 if (obj->options.defaultvol.loaded) { 1948 changed = volfile_changed(&obj->options.defaultvol); 1949 } 1950 if (obj->options.systemvol.loaded) { 1951 changed |= volfile_changed(&obj->options.systemvol); 1952 } 1953 if (obj->options.uservol.loaded) { 1954 changed |= volfile_changed(&obj->options.uservol); 1955 } 1956 if (!changed) 1957 return; 1958 1959 free_extmap(); 1960 free_volumes(); 1961 } 1962 1963 if (parent_or_child == 0) { 1964 LOG(log_debug, logtype_afpd, "load_volumes: AFP MASTER"); 1965 } else { 1966 LOG(log_debug, logtype_afpd, "load_volumes: user: %s", obj->username); 1967 if ((pwent = getpwnam(obj->username))) { 1968 seteuid(0); 1969 ret = set_groups(obj, pwent); 1970 seteuid(obj->uid); 1971 if (ret != 0) { 1972 LOG(log_error, logtype_afpd, "load_volumes: set_groups: %s", strerror(errno)); 1973 return; 1974 } 1975 } 1976 LOG(log_debug, logtype_afpd, "login: supplementary groups: %s", print_groups(ngroups, groups)); 1977 } 1978 1979 if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) { 1980 readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent); 1981 } 1982 1983 if ((*obj->username == '\0') || (obj->options.flags & OPTION_NOUSERVOL)) { 1984 readvolfile(obj, &obj->options.defaultvol, NULL, 1, pwent); 1985 } else if (pwent) { 1986 /* 1987 * Read user's AppleVolumes or .AppleVolumes file 1988 * If neither are readable, read the default volumes file. if 1989 * that doesn't work, create a user share. 1990 */ 1991 if (obj->options.uservol.name) { 1992 free(obj->options.uservol.name); 1993 } 1994 obj->options.uservol.name = strdup(pwent->pw_dir); 1995 if ( readvolfile(obj, &obj->options.uservol, "AppleVolumes", 1, pwent) < 0 && 1996 readvolfile(obj, &obj->options.uservol, ".AppleVolumes", 1, pwent) < 0 && 1997 readvolfile(obj, &obj->options.uservol, "applevolumes", 1, pwent) < 0 && 1998 readvolfile(obj, &obj->options.uservol, ".applevolumes", 1, pwent) < 0 && 1999 obj->options.defaultvol.name != NULL ) { 2000 if (readvolfile(obj, &obj->options.defaultvol, NULL, 1, pwent) < 0) 2001 creatvol(obj, pwent, pwent->pw_dir, NULL, NULL, 1); 2002 } 2003 } 2004 if ( obj->options.flags & OPTION_USERVOLFIRST ) { 2005 readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent ); 2006 } 2007 2008 if ( obj->options.closevol ) { 2009 struct vol *vol; 2010 2011 for ( vol = Volumes; vol; vol = vol->v_next ) { 2012 if (vol->v_deleted && !vol->v_new ) { 2013 of_closevol(vol); 2014 deletevol(vol); 2015 vol = Volumes; 2016 } 2017 } 2018 } 2019} 2020 2021/* ------------------------------- */ 2022int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) 2023{ 2024 struct timeval tv; 2025 struct stat st; 2026 struct vol *volume; 2027 char *data; 2028 char *namebuf; 2029 int vcnt; 2030 size_t len; 2031 2032 load_volumes(obj); 2033 2034 data = rbuf + 5; 2035 for ( vcnt = 0, volume = Volumes; volume; volume = volume->v_next ) { 2036 if (!(volume->v_flags & AFPVOL_NOSTAT)) { 2037 struct maccess ma; 2038 2039 if ( stat( volume->v_path, &st ) < 0 ) { 2040 LOG(log_info, logtype_afpd, "afp_getsrvrparms(%s): stat: %s", 2041 volume->v_path, strerror(errno) ); 2042 continue; /* can't access directory */ 2043 } 2044 if (!S_ISDIR(st.st_mode)) { 2045 continue; /* not a dir */ 2046 } 2047 accessmode(volume, volume->v_path, &ma, NULL, &st); 2048 if ((ma.ma_user & (AR_UREAD | AR_USEARCH)) != (AR_UREAD | AR_USEARCH)) { 2049 continue; /* no r-x access */ 2050 } 2051 } 2052 if (volume->v_hide) { 2053 continue; /* config file changed but the volume was mounted */ 2054 } 2055 2056 if (utf8_encoding()) { 2057 len = ucs2_to_charset_allocate(CH_UTF8_MAC, &namebuf, volume->v_u8mname); 2058 } else { 2059 len = ucs2_to_charset_allocate(obj->options.maccharset, &namebuf, volume->v_macname); 2060 } 2061 2062 if (len == (size_t)-1) 2063 continue; 2064 2065 /* set password bit if there's a volume password */ 2066 *data = (volume->v_password) ? AFPSRVR_PASSWD : 0; 2067 2068 /* Apple 2 clients running ProDOS-8 expect one volume to have 2069 bit 0 of this byte set. They will not recognize anything 2070 on the server unless this is the case. I have not 2071 completely worked this out, but it's related to booting 2072 from the server. Support for that function is a ways 2073 off.. <shirsch@ibm.net> */ 2074 *data |= (volume->v_flags & AFPVOL_A2VOL) ? AFPSRVR_CONFIGINFO : 0; 2075 *data++ |= 0; /* UNIX PRIVS BIT ..., OSX doesn't seem to use it, so we don't either */ 2076 *data++ = len; 2077 memcpy(data, namebuf, len ); 2078 data += len; 2079 free(namebuf); 2080 vcnt++; 2081 } 2082 2083 *rbuflen = data - rbuf; 2084 data = rbuf; 2085 if ( gettimeofday( &tv, NULL ) < 0 ) { 2086 LOG(log_error, logtype_afpd, "afp_getsrvrparms(%s): gettimeofday: %s", volume->v_path, strerror(errno) ); 2087 *rbuflen = 0; 2088 return AFPERR_PARAM; 2089 } 2090 tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec); 2091 memcpy(data, &tv.tv_sec, sizeof( u_int32_t)); 2092 data += sizeof( u_int32_t); 2093 *data = vcnt; 2094 return( AFP_OK ); 2095} 2096 2097/* ------------------------- */ 2098static int volume_codepage(AFPObj *obj, struct vol *volume) 2099{ 2100 struct charset_functions *charset; 2101 /* Codepages */ 2102 2103 if (!volume->v_volcodepage) 2104 volume->v_volcodepage = strdup("UTF8"); 2105 2106 if ( (charset_t) -1 == ( volume->v_volcharset = add_charset(volume->v_volcodepage)) ) { 2107 LOG (log_error, logtype_afpd, "Setting codepage %s as volume codepage failed", volume->v_volcodepage); 2108 return -1; 2109 } 2110 2111 if ( NULL == (charset = find_charset_functions(volume->v_volcodepage)) || charset->flags & CHARSET_ICONV ) { 2112 LOG (log_warning, logtype_afpd, "WARNING: volume encoding %s is *not* supported by netatalk, expect problems !!!!", volume->v_volcodepage); 2113 } 2114 2115 if (!volume->v_maccodepage) 2116 volume->v_maccodepage = strdup(obj->options.maccodepage); 2117 2118 if ( (charset_t) -1 == ( volume->v_maccharset = add_charset(volume->v_maccodepage)) ) { 2119 LOG (log_error, logtype_afpd, "Setting codepage %s as mac codepage failed", volume->v_maccodepage); 2120 return -1; 2121 } 2122 2123 if ( NULL == ( charset = find_charset_functions(volume->v_maccodepage)) || ! (charset->flags & CHARSET_CLIENT) ) { 2124 LOG (log_error, logtype_afpd, "Fatal error: mac charset %s not supported", volume->v_maccodepage); 2125 return -1; 2126 } 2127 volume->v_kTextEncoding = htonl(charset->kTextEncoding); 2128 return 0; 2129} 2130 2131/* ------------------------- */ 2132static int volume_openDB(struct vol *volume) 2133{ 2134 int flags = 0; 2135 2136 if ((volume->v_flags & AFPVOL_NODEV)) { 2137 flags |= CNID_FLAG_NODEV; 2138 } 2139 2140 if (volume->v_cnidscheme == NULL) { 2141 volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME); 2142 LOG(log_info, logtype_afpd, "Volume %s use CNID scheme %s.", 2143 volume->v_path, volume->v_cnidscheme); 2144 } 2145 2146 LOG(log_info, logtype_afpd, "CNID server: %s:%s", 2147 volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv, 2148 volume->v_cnidport ? volume->v_cnidport : Cnid_port); 2149 2150#if 0 2151/* Found this in branch dir-rewrite, maybe we want to use it sometimes */ 2152 2153 /* Legacy pre 2.1 way of sharing eg CD-ROM */ 2154 if (strcmp(volume->v_cnidscheme, "last") == 0) { 2155 /* "last" is gone. We support it by switching to in-memory "tdb" */ 2156 volume->v_cnidscheme = strdup("tdb"); 2157 flags |= CNID_FLAG_MEMORY; 2158 } 2159 2160 /* New way of sharing CD-ROM */ 2161 if (volume->v_flags & AFPVOL_CDROM) { 2162 flags |= CNID_FLAG_MEMORY; 2163 if (strcmp(volume->v_cnidscheme, "tdb") != 0) { 2164 free(volume->v_cnidscheme); 2165 volume->v_cnidscheme = strdup("tdb"); 2166 LOG(log_info, logtype_afpd, "Volume %s is ejectable, switching to scheme %s.", 2167 volume->v_path, volume->v_cnidscheme); 2168 } 2169 } 2170#endif 2171 2172 volume->v_cdb = cnid_open(volume->v_path, 2173 volume->v_umask, 2174 volume->v_cnidscheme, 2175 flags, 2176 volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv, 2177 volume->v_cnidport ? volume->v_cnidport : Cnid_port); 2178 2179 if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) { 2180 /* The first attempt failed and it wasn't yet an attempt to open in-memory */ 2181 LOG(log_error, logtype_afpd, "Can't open volume \"%s\" CNID backend \"%s\" ", 2182 volume->v_path, volume->v_cnidscheme); 2183 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.", 2184 volume->v_path); 2185printf("Reopen volume %s using in memory temporary CNID DB.", 2186 volume->v_path); 2187 flags |= CNID_FLAG_MEMORY; 2188 volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL); 2189//#ifdef SERVERTEXT 2190#if 0 2191 /* kill ourself with SIGUSR2 aka msg pending */ 2192 if (volume->v_cdb) { 2193 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead." 2194 "Check server messages for details!"); 2195 kill(getpid(), SIGUSR2); 2196 /* deactivate cnid caching/storing in AppleDouble files */ 2197 volume->v_flags &= ~AFPVOL_CACHE; 2198 } 2199#endif 2200 } 2201 2202 return (!volume->v_cdb)?-1:0; 2203} 2204 2205/* 2206 Check if the underlying filesystem supports EAs for ea:sys volumes. 2207 If not, switch to ea:ad. 2208 As we can't check (requires write access) on ro-volumes, we switch ea:auto 2209 volumes that are options:ro to ea:none. 2210*/ 2211static void check_ea_sys_support(struct vol *vol) 2212{ 2213 uid_t process_uid = 0; 2214 char eaname[] = {"org.netatalk.supports-eas.XXXXXX"}; 2215 const char *eacontent = "yes"; 2216 2217 if (vol->v_vfs_ea == AFPVOL_EA_AUTO) { 2218 2219 if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) { 2220 LOG(log_info, logtype_afpd, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname); 2221 vol->v_vfs_ea = AFPVOL_EA_NONE; 2222 return; 2223 } 2224 2225 mktemp(eaname); 2226 2227 process_uid = geteuid(); 2228 if (process_uid) 2229 if (seteuid(0) == -1) { 2230 LOG(log_error, logtype_afpd, "check_ea_sys_support: can't seteuid(0): %s", strerror(errno)); 2231 exit(EXITERR_SYS); 2232 } 2233 2234 if ((sys_setxattr(vol->v_path, eaname, eacontent, 4, 0)) == 0) { 2235 sys_removexattr(vol->v_path, eaname); 2236 vol->v_vfs_ea = AFPVOL_EA_SYS; 2237 } else { 2238 LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes, using ea:ad instead", 2239 vol->v_localname); 2240 vol->v_vfs_ea = AFPVOL_EA_AD; 2241 } 2242 2243 if (process_uid) { 2244 if (seteuid(process_uid) == -1) { 2245 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno)); 2246 exit(EXITERR_SYS); 2247 } 2248 } 2249 } 2250} 2251 2252/* ------------------------- 2253 * we are the user here 2254 */ 2255int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) 2256{ 2257 struct stat st; 2258 char *volname; 2259 char *p; 2260 2261 struct vol *volume; 2262 struct dir *dir; 2263 int len, ret; 2264 size_t namelen; 2265 u_int16_t bitmap; 2266 char path[ MAXPATHLEN + 1]; 2267 char *vol_uname; 2268 char *vol_mname; 2269 char *volname_tmp; 2270 2271 ibuf += 2; 2272 memcpy(&bitmap, ibuf, sizeof( bitmap )); 2273 bitmap = ntohs( bitmap ); 2274 ibuf += sizeof( bitmap ); 2275 2276 *rbuflen = 0; 2277 if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) { 2278 return AFPERR_BITMAP; 2279 } 2280 2281 len = (unsigned char)*ibuf++; 2282 volname = obj->oldtmp; 2283 2284 if ((volname_tmp = strchr(volname,'+')) != NULL) 2285 volname = volname_tmp+1; 2286 2287 if (utf8_encoding()) { 2288 namelen = convert_string(CH_UTF8_MAC, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp)); 2289 } else { 2290 namelen = convert_string(obj->options.maccharset, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp)); 2291 } 2292 2293 if ( namelen <= 0) { 2294 return AFPERR_PARAM; 2295 } 2296 2297 ibuf += len; 2298 if ((len + 1) & 1) /* pad to an even boundary */ 2299 ibuf++; 2300 2301 load_volumes(obj); 2302 2303 for ( volume = Volumes; volume; volume = volume->v_next ) { 2304 if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) { 2305 break; 2306 } 2307 } 2308 2309 if ( volume == NULL ) { 2310 return AFPERR_PARAM; 2311 } 2312 2313 /* check for a volume password */ 2314 if (volume->v_password && strncmp(ibuf, volume->v_password, VOLPASSLEN)) { 2315 return AFPERR_ACCESS; 2316 } 2317 2318 if (( volume->v_flags & AFPVOL_OPEN ) ) { 2319 /* the volume is already open */ 2320#ifdef FORCE_UIDGID 2321 set_uidgid ( volume ); 2322#endif 2323 return stat_vol(bitmap, volume, rbuf, rbuflen); 2324 } 2325 2326 if (volume->v_root_preexec) { 2327 if ((ret = afprun(1, volume->v_root_preexec, NULL)) && volume->v_root_preexec_close) { 2328 LOG(log_error, logtype_afpd, "afp_openvol(%s): root preexec : %d", volume->v_path, ret ); 2329 return AFPERR_MISC; 2330 } 2331 } 2332 2333#ifdef FORCE_UIDGID 2334 set_uidgid ( volume ); 2335#endif 2336 2337 if (volume->v_preexec) { 2338 if ((ret = afprun(0, volume->v_preexec, NULL)) && volume->v_preexec_close) { 2339 LOG(log_error, logtype_afpd, "afp_openvol(%s): preexec : %d", volume->v_path, ret ); 2340 return AFPERR_MISC; 2341 } 2342 } 2343 2344 if ( stat( volume->v_path, &st ) < 0 ) { 2345 return AFPERR_PARAM; 2346 } 2347 2348 if ( chdir( volume->v_path ) < 0 ) { 2349 return AFPERR_PARAM; 2350 } 2351 2352 if ( NULL == getcwd(path, MAXPATHLEN)) { 2353 /* shouldn't be fatal but it will fail later */ 2354 LOG(log_error, logtype_afpd, "afp_openvol(%s): volume pathlen too long", volume->v_path); 2355 return AFPERR_MISC; 2356 } 2357 2358 /* Normalize volume path */ 2359#ifdef REALPATH_TAKES_NULL 2360 if ((volume->v_path = realpath(path, NULL)) == NULL) 2361 return AFPERR_MISC; 2362#else 2363 if ((volume->v_path = malloc(MAXPATHLEN+1)) == NULL) 2364 return AFPERR_MISC; 2365 if (realpath(path, volume->v_path) == NULL) { 2366 free(volume->v_path); 2367 return AFPERR_MISC; 2368 } 2369 /* Safe some memory */ 2370 char *tmp; 2371 if ((tmp = strdup(volume->v_path)) == NULL) { 2372 free(volume->v_path); 2373 return AFPERR_MISC; 2374 } 2375 free(volume->v_path); 2376 volume->v_path = tmp; 2377#endif 2378 2379 if (volume_codepage(obj, volume) < 0) { 2380 ret = AFPERR_MISC; 2381 goto openvol_err; 2382 } 2383 2384 /* initialize volume variables 2385 * FIXME file size 2386 */ 2387 if (utf8_encoding()) { 2388 volume->max_filename = UTF8FILELEN_EARLY; 2389 } 2390 else { 2391 volume->max_filename = MACFILELEN; 2392 } 2393 2394 volume->v_flags |= AFPVOL_OPEN; 2395 volume->v_cdb = NULL; 2396 2397 if (utf8_encoding()) { 2398 len = convert_string_allocate(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, namelen, &vol_mname); 2399 } else { 2400 len = convert_string_allocate(CH_UCS2, obj->options.maccharset, volume->v_macname, namelen, &vol_mname); 2401 } 2402 if ( !vol_mname || len <= 0) { 2403 ret = AFPERR_MISC; 2404 goto openvol_err; 2405 } 2406 2407 if ((vol_uname = strrchr(path, '/')) == NULL) 2408 vol_uname = path; 2409 else if (*(vol_uname + 1) != '\0') 2410 vol_uname++; 2411 2412 if ((dir = dir_new(vol_mname, 2413 vol_uname, 2414 volume, 2415 DIRDID_ROOT_PARENT, 2416 DIRDID_ROOT, 2417 bfromcstr(volume->v_path), 2418 &st) 2419 ) == NULL) { 2420 free(vol_mname); 2421 LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) ); 2422 ret = AFPERR_MISC; 2423 goto openvol_err; 2424 } 2425 free(vol_mname); 2426 volume->v_root = dir; 2427 curdir = dir; 2428 2429 if (volume_openDB(volume) < 0) { 2430 LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s", 2431 volume->v_path, volume->v_cnidscheme); 2432 ret = AFPERR_MISC; 2433 goto openvol_err; 2434 } 2435 2436 ret = stat_vol(bitmap, volume, rbuf, rbuflen); 2437 2438 if (ret == AFP_OK) { 2439 handle_special_folders(volume); 2440 savevolinfo(volume, 2441 volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv, 2442 volume->v_cnidport ? volume->v_cnidport : Cnid_port); 2443 2444 2445 /* 2446 * If you mount a volume twice, the second time the trash appears on 2447 * the desk-top. That's because the Mac remembers the DID for the 2448 * trash (even for volumes in different zones, on different servers). 2449 * Just so this works better, we prime the DID cache with the trash, 2450 * fixing the trash at DID 17. 2451 * FIXME (RL): should it be done inside a CNID backend ? (always returning Trash DID when asked) ? 2452 */ 2453 if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) { 2454 2455 /* FIXME find db time stamp */ 2456 if (cnid_getstamp(volume->v_cdb, volume->v_stamp, sizeof(volume->v_stamp)) < 0) { 2457 LOG (log_error, logtype_afpd, 2458 "afp_openvol(%s): Fatal error: Unable to get stamp value from CNID backend", 2459 volume->v_path); 2460 ret = AFPERR_MISC; 2461 goto openvol_err; 2462 } 2463 } 2464 else { 2465 p = Trash; 2466 cname( volume, volume->v_root, &p ); 2467 } 2468 return( AFP_OK ); 2469 } 2470 2471openvol_err: 2472 if (volume->v_root) { 2473 dir_free( volume->v_root ); 2474 volume->v_root = NULL; 2475 } 2476 2477 volume->v_flags &= ~AFPVOL_OPEN; 2478 if (volume->v_cdb != NULL) { 2479 cnid_close(volume->v_cdb); 2480 volume->v_cdb = NULL; 2481 } 2482 *rbuflen = 0; 2483 return ret; 2484} 2485 2486/* ------------------------- */ 2487static void closevol(struct vol *vol) 2488{ 2489 if (!vol) 2490 return; 2491 2492 dir_free( vol->v_root ); 2493 vol->v_root = NULL; 2494 if (vol->v_cdb != NULL) { 2495 cnid_close(vol->v_cdb); 2496 vol->v_cdb = NULL; 2497 } 2498 2499 if (vol->v_postexec) { 2500 afprun(0, vol->v_postexec, NULL); 2501 } 2502 if (vol->v_root_postexec) { 2503 afprun(1, vol->v_root_postexec, NULL); 2504 } 2505} 2506 2507/* ------------------------- */ 2508void close_all_vol(void) 2509{ 2510 struct vol *ovol; 2511 curdir = NULL; 2512 for ( ovol = Volumes; ovol; ovol = ovol->v_next ) { 2513 if ( (ovol->v_flags & AFPVOL_OPEN) ) { 2514 ovol->v_flags &= ~AFPVOL_OPEN; 2515 closevol(ovol); 2516 } 2517 } 2518} 2519 2520/* ------------------------- */ 2521static void deletevol(struct vol *vol) 2522{ 2523 struct vol *ovol; 2524 2525 vol->v_flags &= ~AFPVOL_OPEN; 2526 for ( ovol = Volumes; ovol; ovol = ovol->v_next ) { 2527 if ( (ovol->v_flags & AFPVOL_OPEN) ) { 2528 break; 2529 } 2530 } 2531 if ( ovol != NULL ) { 2532 /* Even if chdir fails, we can't say afp_closevol fails. */ 2533 if ( chdir( ovol->v_path ) == 0 ) { 2534 curdir = ovol->v_root; 2535 } 2536 } 2537 2538 closevol(vol); 2539 if (vol->v_deleted) { 2540 showvol(vol->v_name); 2541 volume_free(vol); 2542 volume_unlink(vol); 2543 free(vol); 2544 } 2545} 2546 2547/* ------------------------- */ 2548int afp_closevol(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) 2549{ 2550 struct vol *vol; 2551 u_int16_t vid; 2552 2553 *rbuflen = 0; 2554 ibuf += 2; 2555 memcpy(&vid, ibuf, sizeof( vid )); 2556 if (NULL == ( vol = getvolbyvid( vid )) ) { 2557 return( AFPERR_PARAM ); 2558 } 2559 2560 deletevol(vol); 2561 current_vol = NULL; 2562 2563 return( AFP_OK ); 2564} 2565 2566/* ------------------------- */ 2567struct vol *getvolbyvid(const u_int16_t vid ) 2568{ 2569 struct vol *vol; 2570 2571 for ( vol = Volumes; vol; vol = vol->v_next ) { 2572 if ( vid == vol->v_vid ) { 2573 break; 2574 } 2575 } 2576 if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) { 2577 return( NULL ); 2578 } 2579 2580#ifdef FORCE_UIDGID 2581 set_uidgid ( vol ); 2582#endif /* FORCE_UIDGID */ 2583 2584 current_vol = vol; 2585 2586 return( vol ); 2587} 2588 2589/* ------------------------ */ 2590static int ext_cmp_key(const void *key, const void *obj) 2591{ 2592 const char *p = key; 2593 const struct extmap *em = obj; 2594 return strdiacasecmp(p, em->em_ext); 2595} 2596struct extmap *getextmap(const char *path) 2597{ 2598 char *p; 2599 struct extmap *em; 2600 2601 if (!Extmap_cnt || NULL == ( p = strrchr( path, '.' )) ) { 2602 return( Defextmap ); 2603 } 2604 p++; 2605 if (!*p) { 2606 return( Defextmap ); 2607 } 2608 em = bsearch(p, Extmap, Extmap_cnt, sizeof(struct extmap), ext_cmp_key); 2609 if (em) { 2610 return( em ); 2611 } else { 2612 return( Defextmap ); 2613 } 2614} 2615 2616/* ------------------------- */ 2617struct extmap *getdefextmap(void) 2618{ 2619 return( Defextmap ); 2620} 2621 2622/* -------------------------- 2623 poll if a volume is changed by other processes. 2624 return 2625 0 no attention msg sent 2626 1 attention msg sent 2627 -1 error (socket closed) 2628 2629 Note: if attention return -1 no packet has been 2630 sent because the buffer is full, we don't care 2631 either there's no reader or there's a lot of 2632 traffic and another pollvoltime will follow 2633*/ 2634int pollvoltime(AFPObj *obj) 2635 2636{ 2637 struct vol *vol; 2638 struct timeval tv; 2639 struct stat st; 2640 2641 if (!(afp_version > 21 && obj->options.server_notif)) 2642 return 0; 2643 2644 if ( gettimeofday( &tv, NULL ) < 0 ) 2645 return 0; 2646 2647 for ( vol = Volumes; vol; vol = vol->v_next ) { 2648 if ( (vol->v_flags & AFPVOL_OPEN) && vol->v_mtime + 30 < tv.tv_sec) { 2649 if ( !stat( vol->v_path, &st ) && vol->v_mtime != st.st_mtime ) { 2650 vol->v_mtime = st.st_mtime; 2651 if (!obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED)) 2652 return -1; 2653 return 1; 2654 } 2655 } 2656 } 2657 return 0; 2658} 2659 2660/* ------------------------- */ 2661void setvoltime(AFPObj *obj, struct vol *vol) 2662{ 2663 struct timeval tv; 2664 2665 if ( gettimeofday( &tv, NULL ) < 0 ) { 2666 LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) ); 2667 return; 2668 } 2669 if( utime( vol->v_path, NULL ) < 0 ) { 2670 /* write of time failed ... probably a read only filesys, 2671 * where no other users can interfere, so there's no issue here 2672 */ 2673 } 2674 2675 /* a little granularity */ 2676 if (vol->v_mtime < tv.tv_sec) { 2677 vol->v_mtime = tv.tv_sec; 2678 /* or finder doesn't update free space 2679 * AFP 3.2 and above clients seem to be ok without so many notification 2680 */ 2681 if (afp_version < 32 && obj->options.server_notif) { 2682 obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED); 2683 } 2684 } 2685} 2686 2687/* ------------------------- */ 2688int afp_getvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,char *rbuf, size_t *rbuflen) 2689{ 2690 struct vol *vol; 2691 u_int16_t vid, bitmap; 2692 2693 ibuf += 2; 2694 memcpy(&vid, ibuf, sizeof( vid )); 2695 ibuf += sizeof( vid ); 2696 memcpy(&bitmap, ibuf, sizeof( bitmap )); 2697 bitmap = ntohs( bitmap ); 2698 2699 if (NULL == ( vol = getvolbyvid( vid )) ) { 2700 *rbuflen = 0; 2701 return( AFPERR_PARAM ); 2702 } 2703 2704 return stat_vol(bitmap, vol, rbuf, rbuflen); 2705} 2706 2707/* ------------------------- */ 2708int afp_setvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) 2709{ 2710 struct adouble ad; 2711 struct vol *vol; 2712 u_int16_t vid, bitmap; 2713 u_int32_t aint; 2714 2715 ibuf += 2; 2716 *rbuflen = 0; 2717 2718 memcpy(&vid, ibuf, sizeof( vid )); 2719 ibuf += sizeof( vid ); 2720 memcpy(&bitmap, ibuf, sizeof( bitmap )); 2721 bitmap = ntohs( bitmap ); 2722 ibuf += sizeof(bitmap); 2723 2724 if (( vol = getvolbyvid( vid )) == NULL ) { 2725 return( AFPERR_PARAM ); 2726 } 2727 2728 if ((vol->v_flags & AFPVOL_RO)) 2729 return AFPERR_VLOCK; 2730 2731 /* we can only set the backup date. */ 2732 if (bitmap != (1 << VOLPBIT_BDATE)) 2733 return AFPERR_BITMAP; 2734 2735 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 2736 if ( ad_open( vol->v_path, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 2737 0666, &ad) < 0 ) { 2738 if (errno == EROFS) 2739 return AFPERR_VLOCK; 2740 2741 return AFPERR_ACCESS; 2742 } 2743 2744 memcpy(&aint, ibuf, sizeof(aint)); 2745 ad_setdate(&ad, AD_DATE_BACKUP, aint); 2746 ad_flush(&ad); 2747 ad_close(&ad, ADFLAGS_HF); 2748 return( AFP_OK ); 2749} 2750 2751/* ------------------------- */ 2752int wincheck(const struct vol *vol, const char *path) 2753{ 2754 int len; 2755 2756 if (!(vol->v_flags & AFPVOL_MSWINDOWS)) 2757 return 1; 2758 2759 /* empty paths are not allowed */ 2760 if ((len = strlen(path)) == 0) 2761 return 0; 2762 2763 /* leading or trailing whitespaces are not allowed, carriage returns 2764 * and probably other whitespace is okay, tabs are not allowed 2765 */ 2766 if ((path[0] == ' ') || (path[len-1] == ' ')) 2767 return 0; 2768 2769 /* certain characters are not allowed */ 2770 if (strpbrk(path, MSWINDOWS_BADCHARS)) 2771 return 0; 2772 2773 /* everything else is okay */ 2774 return 1; 2775} 2776 2777 2778/* 2779 * precreate a folder 2780 * this is only intended for folders in the volume root 2781 * It will *not* work if the folder name contains extended characters 2782 */ 2783static int create_special_folder (const struct vol *vol, const struct _special_folder *folder) 2784{ 2785 char *p,*q,*r; 2786 struct adouble ad; 2787 u_int16_t attr; 2788 struct stat st; 2789 int ret; 2790 2791 2792 p = (char *) malloc ( strlen(vol->v_path)+strlen(folder->name)+2); 2793 if ( p == NULL) { 2794 LOG(log_error, logtype_afpd,"malloc failed"); 2795 exit (EXITERR_SYS); 2796 } 2797 2798 q=strdup(folder->name); 2799 if ( q == NULL) { 2800 LOG(log_error, logtype_afpd,"malloc failed"); 2801 exit (EXITERR_SYS); 2802 } 2803 2804 strcpy(p, vol->v_path); 2805 strcat(p, "/"); 2806 2807 r=q; 2808 while (*r) { 2809 if ((vol->v_casefold & AFPVOL_MTOUUPPER)) 2810 *r=toupper(*r); 2811 else if ((vol->v_casefold & AFPVOL_MTOULOWER)) 2812 *r=tolower(*r); 2813 r++; 2814 } 2815 strcat(p, q); 2816 2817 if ( (ret = stat( p, &st )) < 0 ) { 2818 if (folder->precreate) { 2819 if (ad_mkdir(p, folder->mode)) { 2820 LOG(log_debug, logtype_afpd,"Creating '%s' failed in %s: %s", p, vol->v_path, strerror(errno)); 2821 free(p); 2822 free(q); 2823 return -1; 2824 } 2825 ret = 0; 2826 } 2827 } 2828 2829 if ( !ret && folder->hide) { 2830 /* Hide it */ 2831 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 2832 if (ad_open_metadata( p, ADFLAGS_DIR, O_CREAT, &ad) < 0) { 2833 free (p); 2834 free(q); 2835 return (-1); 2836 } 2837 2838 ad_setname(&ad, folder->name); 2839 2840 ad_getattr(&ad, &attr); 2841 attr |= htons( ntohs( attr ) | ATTRBIT_INVISIBLE ); 2842 ad_setattr(&ad, attr); 2843 2844 /* do the same with the finder info */ 2845 if (ad_entry(&ad, ADEID_FINDERI)) { 2846 memcpy(&attr, ad_entry(&ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, sizeof(attr)); 2847 attr |= htons(FINDERINFO_INVISIBLE); 2848 memcpy(ad_entry(&ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,&attr, sizeof(attr)); 2849 } 2850 2851 ad_flush( &ad ); 2852 ad_close_metadata( &ad); 2853 } 2854 free(p); 2855 free(q); 2856 return 0; 2857} 2858 2859static void handle_special_folders (const struct vol * vol) 2860{ 2861 const _special_folder *p = &special_folders[0]; 2862 uid_t process_uid; 2863 2864 process_uid = geteuid(); 2865 if (process_uid) { 2866 if (seteuid(0) == -1) { 2867 process_uid = 0; 2868 } 2869 } 2870 2871 for (; p->name != NULL; p++) { 2872 create_special_folder (vol, p); 2873 } 2874 2875 if (process_uid) { 2876 if (seteuid(process_uid) == -1) { 2877 LOG(log_error, logtype_logger, "can't seteuid back %s", strerror(errno)); 2878 exit(EXITERR_SYS); 2879 } 2880 } 2881} 2882 2883const struct vol *getvolumes(void) 2884{ 2885 return Volumes; 2886} 2887 2888void unload_volumes_and_extmap(void) 2889{ 2890 LOG(log_debug, logtype_afpd, "unload_volumes_and_extmap"); 2891 free_extmap(); 2892 free_volumes(); 2893} 2894 2895/* 2896 * Get a volumes UUID from the config file. 2897 * If there is none, it is generated and stored there. 2898 * 2899 * Returns pointer to allocated storage on success, NULL on error. 2900 */ 2901static char *get_vol_uuid(const AFPObj *obj, const char *volname) 2902{ 2903 char *volname_conf; 2904 char buf[1024], uuid[UUID_PRINTABLE_STRING_LENGTH], *p; 2905 FILE *fp; 2906 struct stat tmpstat; 2907 int fd; 2908 2909 if ((fp = fopen(obj->options.uuidconf, "r")) != NULL) { /* read open? */ 2910 /* scan in the conf file */ 2911 while (fgets(buf, sizeof(buf), fp) != NULL) { 2912 p = buf; 2913 while (p && isblank(*p)) 2914 p++; 2915 if (!p || (*p == '#') || (*p == '\n')) 2916 continue; /* invalid line */ 2917 if (*p == '"') { 2918 p++; 2919 if ((volname_conf = strtok( p, "\"" )) == NULL) 2920 continue; /* syntax error */ 2921 } else { 2922 if ((volname_conf = strtok( p, " \t" )) == NULL) 2923 continue; /* syntax error: invalid name */ 2924 } 2925 p = strchr(p, '\0'); 2926 p++; 2927 if (*p == '\0') 2928 continue; /* syntax error */ 2929 2930 if (strcmp(volname, volname_conf) != 0) 2931 continue; /* another volume name */ 2932 2933 while (p && isblank(*p)) 2934 p++; 2935 2936 if (sscanf(p, "%36s", uuid) == 1 ) { 2937 for (int i=0; uuid[i]; i++) 2938 uuid[i] = toupper(uuid[i]); 2939 LOG(log_debug, logtype_afpd, "get_uuid('%s'): UUID: '%s'", volname, uuid); 2940 fclose(fp); 2941 return strdup(uuid); 2942 } 2943 } 2944 } 2945 2946 if (fp) 2947 fclose(fp); 2948 2949 /* not found or no file, reopen in append mode */ 2950 2951 if (stat(obj->options.uuidconf, &tmpstat)) { /* no file */ 2952 if (( fd = creat(obj->options.uuidconf, 0644 )) < 0 ) { 2953 LOG(log_error, logtype_afpd, "ERROR: Cannot create %s (%s).", 2954 obj->options.uuidconf, strerror(errno)); 2955 return NULL; 2956 } 2957 if (( fp = fdopen( fd, "w" )) == NULL ) { 2958 LOG(log_error, logtype_afpd, "ERROR: Cannot fdopen %s (%s).", 2959 obj->options.uuidconf, strerror(errno)); 2960 close(fd); 2961 return NULL; 2962 } 2963 } else if ((fp = fopen(obj->options.uuidconf, "a+")) == NULL) { /* not found */ 2964 LOG(log_error, logtype_afpd, "Cannot create or append to %s (%s).", 2965 obj->options.uuidconf, strerror(errno)); 2966 return NULL; 2967 } 2968 fseek(fp, 0L, SEEK_END); 2969 if(ftell(fp) == 0) { /* size = 0 */ 2970 fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n"); 2971 fprintf(fp, "# This file is auto-generated by afpd\n"); 2972 fprintf(fp, "# and stores UUIDs for TM volumes.\n\n"); 2973 } else { 2974 fseek(fp, -1L, SEEK_END); 2975 if(fgetc(fp) != '\n') fputc('\n', fp); /* last char is \n? */ 2976 } 2977 2978 /* generate uuid and write to file */ 2979 atalk_uuid_t id; 2980 const char *cp; 2981 randombytes((void *)id, 16); 2982 cp = uuid_bin2string(id); 2983 2984 LOG(log_debug, logtype_afpd, "get_uuid('%s'): generated UUID '%s'", volname, cp); 2985 2986 fprintf(fp, "\"%s\"\t%36s\n", volname, cp); 2987 fclose(fp); 2988 2989 return strdup(cp); 2990} 2991