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