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#include <string.h> 18#include <sys/param.h> 19#include <sys/socket.h> 20#include <netinet/in.h> 21#include <arpa/inet.h> 22#include <inttypes.h> 23#include <time.h> 24 25#include <atalk/dsi.h> 26#include <atalk/adouble.h> 27#include <atalk/afp.h> 28#include <atalk/util.h> 29#include <atalk/logger.h> 30#include <atalk/vfs.h> 31#include <atalk/uuid.h> 32#include <atalk/ea.h> 33#include <atalk/bstrlib.h> 34#include <atalk/bstradd.h> 35#include <atalk/ftw.h> 36#include <atalk/globals.h> 37#include <atalk/fce_api.h> 38#include <atalk/errchk.h> 39#include <atalk/iniparser.h> 40#include <atalk/unix.h> 41#include <atalk/netatalk_conf.h> 42#include <atalk/server_ipc.h> 43 44#ifdef CNID_DB 45#include <atalk/cnid.h> 46#endif /* CNID_DB*/ 47 48#include "directory.h" 49#include "file.h" 50#include "volume.h" 51#include "unix.h" 52#include "mangle.h" 53#include "fork.h" 54#include "hash.h" 55#include "acls.h" 56 57#define VOLPASSLEN 8 58 59extern int afprun(int root, char *cmd, int *outfd); 60 61/*! 62 * Read band-size info from Info.plist XML file of an TM sparsebundle 63 * 64 * @param path (r) path to Info.plist file 65 * @return band-size in bytes, -1 on error 66 */ 67static long long int get_tm_bandsize(const char *path) 68{ 69 EC_INIT; 70 FILE *file = NULL; 71 char buf[512]; 72 long long int bandsize = -1; 73 74 EC_NULL_LOGSTR( file = fopen(path, "r"), 75 "get_tm_bandsize(\"%s\"): %s", 76 path, strerror(errno) ); 77 78 while (fgets(buf, sizeof(buf), file) != NULL) { 79 if (strstr(buf, "band-size") == NULL) 80 continue; 81 82 if (fscanf(file, " <integer>%lld</integer>", &bandsize) != 1) { 83 LOG(log_error, logtype_afpd, "get_tm_bandsize(\"%s\"): can't parse band-size", path); 84 EC_FAIL; 85 } 86 break; 87 } 88 89EC_CLEANUP: 90 if (file) 91 fclose(file); 92 LOG(log_debug, logtype_afpd, "get_tm_bandsize(\"%s\"): bandsize: %lld", path, bandsize); 93 return bandsize; 94} 95 96/*! 97 * Return number on entries in a directory 98 * 99 * @param path (r) path to dir 100 * @return number of entries, -1 on error 101 */ 102static long long int get_tm_bands(const char *path) 103{ 104 EC_INIT; 105 long long int count = 0; 106 DIR *dir = NULL; 107 const struct dirent *entry; 108 109 EC_NULL( dir = opendir(path) ); 110 111 while ((entry = readdir(dir)) != NULL) 112 count++; 113 count -= 2; /* All OSens I'm aware of return "." and "..", so just substract them, avoiding string comparison in loop */ 114 115EC_CLEANUP: 116 if (dir) 117 closedir(dir); 118 if (ret != 0) 119 return -1; 120 return count; 121} 122 123/*! 124 * Calculate used size of a TimeMachine volume 125 * 126 * This assumes that the volume is used only for TimeMachine. 127 * 128 * 1) readdir(path of volume) 129 * 2) for every element that matches regex "\(.*\)\.sparsebundle$" : 130 * 3) parse "\1.sparsebundle/Info.plist" and read the band-size XML key integer value 131 * 4) readdir "\1.sparsebundle/bands/" counting files 132 * 5) calculate used size as: (file_count - 1) * band-size 133 * 134 * The result of the calculation is returned in "volume->v_tm_used". 135 * "volume->v_appended" gets reset to 0. 136 * "volume->v_tm_cachetime" is updated with the current time from time(NULL). 137 * 138 * "volume->v_tm_used" is cached for TM_USED_CACHETIME seconds and updated by 139 * "volume->v_appended". The latter is increased by X every time the client 140 * appends X bytes to a file (in fork.c). 141 * 142 * @param vol (rw) volume to calculate 143 * @return 0 on success, -1 on error 144 */ 145#define TM_USED_CACHETIME 60 /* cache for 60 seconds */ 146static int get_tm_used(struct vol * restrict vol) 147{ 148 EC_INIT; 149 long long int bandsize; 150 VolSpace used = 0; 151 bstring infoplist = NULL; 152 bstring bandsdir = NULL; 153 DIR *dir = NULL; 154 const struct dirent *entry; 155 const char *p; 156 long int links; 157 time_t now = time(NULL); 158 159 if (vol->v_tm_cachetime 160 && ((vol->v_tm_cachetime + TM_USED_CACHETIME) >= now)) { 161 if (vol->v_tm_used == -1) 162 EC_FAIL; 163 vol->v_tm_used += vol->v_appended; 164 vol->v_appended = 0; 165 LOG(log_debug, logtype_afpd, "getused(\"%s\"): cached: %" PRIu64 " bytes", 166 vol->v_path, vol->v_tm_used); 167 return 0; 168 } 169 170 vol->v_tm_cachetime = now; 171 172 EC_NULL( dir = opendir(vol->v_path) ); 173 174 while ((entry = readdir(dir)) != NULL) { 175 if (((p = strstr(entry->d_name, "sparsebundle")) != NULL) 176 && (strlen(entry->d_name) == (p + strlen("sparsebundle") - entry->d_name))) { 177 178 EC_NULL_LOG( infoplist = bformat("%s/%s/%s", vol->v_path, entry->d_name, "Info.plist") ); 179 180 if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1) { 181 bdestroy(infoplist); 182 continue; 183 } 184 185 EC_NULL_LOG( bandsdir = bformat("%s/%s/%s/", vol->v_path, entry->d_name, "bands") ); 186 187 if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1) { 188 bdestroy(infoplist); 189 bdestroy(bandsdir); 190 continue; 191 } 192 193 used += (links - 1) * bandsize; 194 LOG(log_debug, logtype_afpd, "getused(\"%s\"): bands: %" PRIu64 " bytes", 195 cfrombstr(bandsdir), used); 196 } 197 } 198 199 vol->v_tm_used = used; 200 201EC_CLEANUP: 202 if (infoplist) 203 bdestroy(infoplist); 204 if (bandsdir) 205 bdestroy(bandsdir); 206 if (dir) 207 closedir(dir); 208 209 LOG(log_debug, logtype_afpd, "getused(\"%s\"): %" PRIu64 " bytes", vol->v_path, vol->v_tm_used); 210 211 EC_EXIT; 212} 213 214static int getvolspace(const AFPObj *obj, struct vol *vol, 215 uint32_t *bfree, uint32_t *btotal, 216 VolSpace *xbfree, VolSpace *xbtotal, uint32_t *bsize) 217{ 218 int spaceflag, rc; 219 uint32_t maxsize; 220#ifndef NO_QUOTA_SUPPORT 221 VolSpace qfree, qtotal; 222#endif 223 224 spaceflag = AFPVOL_GVSMASK & vol->v_flags; 225 /* report up to 2GB if afp version is < 2.2 (4GB if not) */ 226 maxsize = (obj->afp_version < 22) ? 0x7fffffffL : 0xffffffffL; 227 228#ifdef AFS 229 if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) { 230 if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) { 231 vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS; 232 goto getvolspace_done; 233 } 234 } 235#endif 236 237 if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal, bsize)) != AFP_OK ) { 238 return( rc ); 239 } 240 241#ifndef NO_QUOTA_SUPPORT 242 if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) { 243 if ( uquota_getvolspace(obj, vol, &qfree, &qtotal, *bsize ) == AFP_OK ) { 244 vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA; 245 *xbfree = MIN(*xbfree, qfree); 246 *xbtotal = MIN(*xbtotal, qtotal); 247 goto getvolspace_done; 248 } 249 } 250#endif 251 vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS; 252 253getvolspace_done: 254 if (vol->v_limitsize) { 255 if (get_tm_used(vol) != 0) 256 return AFPERR_MISC; 257 258 *xbtotal = MIN(*xbtotal, (vol->v_limitsize * 1024 * 1024)); 259 *xbfree = MIN(*xbfree, *xbtotal < vol->v_tm_used ? 0 : *xbtotal - vol->v_tm_used); 260 261 LOG(log_debug, logtype_afpd, 262 "volparams: total: %" PRIu64 ", used: %" PRIu64 ", free: %" PRIu64 " bytes", 263 *xbtotal, vol->v_tm_used, *xbfree); 264 } 265 266 *bfree = MIN(*xbfree, maxsize); 267 *btotal = MIN(*xbtotal, maxsize); 268 return( AFP_OK ); 269} 270 271/* ----------------------- 272 * set volume creation date 273 * avoid duplicate, well at least it tries 274 */ 275static void vol_setdate(uint16_t id, struct adouble *adp, time_t date) 276{ 277 struct vol *volume; 278 struct vol *vol = getvolumes(); 279 280 for ( volume = getvolumes(); volume; volume = volume->v_next ) { 281 if (volume->v_vid == id) { 282 vol = volume; 283 } 284 else if ((time_t)(AD_DATE_FROM_UNIX(date)) == volume->v_ctime) { 285 date = date+1; 286 volume = getvolumes(); /* restart */ 287 } 288 } 289 vol->v_ctime = AD_DATE_FROM_UNIX(date); 290 ad_setdate(adp, AD_DATE_CREATE | AD_DATE_UNIX, date); 291} 292 293/* ----------------------- */ 294static int getvolparams(const AFPObj *obj, uint16_t bitmap, struct vol *vol, struct stat *st, char *buf, size_t *buflen) 295{ 296 struct adouble ad; 297 int bit = 0, isad = 1; 298 uint32_t aint; 299 u_short ashort; 300 uint32_t bfree, btotal, bsize; 301 VolSpace xbfree, xbtotal; /* extended bytes */ 302 char *data, *nameoff = NULL; 303 char *slash; 304 305 LOG(log_debug, logtype_afpd, "getvolparams: Volume '%s'", vol->v_localname); 306 307 /* courtesy of jallison@whistle.com: 308 * For MacOS8.x support we need to create the 309 * .Parent file here if it doesn't exist. */ 310 311 /* Convert adouble:v2 to adouble:ea on the fly */ 312 (void)ad_convert(vol->v_path, st, vol, NULL); 313 314 ad_init(&ad, vol); 315 if (ad_open(&ad, vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0 ) { 316 isad = 0; 317 vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime); 318 319 } else if (ad_get_MD_flags( &ad ) & O_CREAT) { 320 slash = strrchr( vol->v_path, '/' ); 321 if(slash) 322 slash++; 323 else 324 slash = vol->v_path; 325 if (ad_getentryoff(&ad, ADEID_NAME)) { 326 ad_setentrylen( &ad, ADEID_NAME, strlen( slash )); 327 memcpy(ad_entry( &ad, ADEID_NAME ), slash, 328 ad_getentrylen( &ad, ADEID_NAME )); 329 } 330 vol_setdate(vol->v_vid, &ad, st->st_mtime); 331 ad_flush(&ad); 332 } 333 else { 334 if (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0) 335 vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime); 336 else 337 vol->v_ctime = aint; 338 } 339 340 if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) | 341 (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) | 342 (1<<VOLPBIT_BSIZE)) ) != 0 ) { 343 if ( getvolspace(obj, vol, &bfree, &btotal, &xbfree, &xbtotal, 344 &bsize) != AFP_OK ) { 345 if ( isad ) { 346 ad_close( &ad, ADFLAGS_HF ); 347 } 348 return( AFPERR_PARAM ); 349 } 350 } 351 352 data = buf; 353 while ( bitmap != 0 ) { 354 while (( bitmap & 1 ) == 0 ) { 355 bitmap = bitmap>>1; 356 bit++; 357 } 358 359 switch ( bit ) { 360 case VOLPBIT_ATTR : 361 ashort = 0; 362 /* check for read-only. 363 * NOTE: we don't actually set the read-only flag unless 364 * it's passed in that way as it's possible to mount 365 * a read-write filesystem under a read-only one. */ 366 if ((vol->v_flags & AFPVOL_RO) || 367 ((utime(vol->v_path, NULL) < 0) && (errno == EROFS))) { 368 ashort |= VOLPBIT_ATTR_RO; 369 } 370 /* prior 2.1 only VOLPBIT_ATTR_RO is defined */ 371 if (obj->afp_version > 20) { 372 if (vol->v_cdb != NULL && (vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) 373 ashort |= VOLPBIT_ATTR_FILEID; 374 ashort |= VOLPBIT_ATTR_CATSEARCH; 375 376 if (obj->afp_version >= 30) { 377 ashort |= VOLPBIT_ATTR_UTF8; 378 if (vol->v_flags & AFPVOL_UNIX_PRIV) 379 ashort |= VOLPBIT_ATTR_UNIXPRIV; 380 if (vol->v_flags & AFPVOL_TM) 381 ashort |= VOLPBIT_ATTR_TM; 382 if (vol->v_flags & AFPVOL_NONETIDS) 383 ashort |= VOLPBIT_ATTR_NONETIDS; 384 if (obj->afp_version >= 32) { 385 if (vol->v_vfs_ea) 386 ashort |= VOLPBIT_ATTR_EXT_ATTRS; 387 if (vol->v_flags & AFPVOL_ACLS) 388 ashort |= VOLPBIT_ATTR_ACLS; 389 } 390 } 391 } 392 ashort = htons(ashort); 393 memcpy(data, &ashort, sizeof( ashort )); 394 data += sizeof( ashort ); 395 break; 396 397 case VOLPBIT_SIG : 398 ashort = htons( AFPVOLSIG_DEFAULT ); 399 memcpy(data, &ashort, sizeof( ashort )); 400 data += sizeof( ashort ); 401 break; 402 403 case VOLPBIT_CDATE : 404 aint = vol->v_ctime; 405 memcpy(data, &aint, sizeof( aint )); 406 data += sizeof( aint ); 407 break; 408 409 case VOLPBIT_MDATE : 410 if ( st->st_mtime > vol->v_mtime ) { 411 vol->v_mtime = st->st_mtime; 412 } 413 aint = AD_DATE_FROM_UNIX(vol->v_mtime); 414 memcpy(data, &aint, sizeof( aint )); 415 data += sizeof( aint ); 416 break; 417 418 case VOLPBIT_BDATE : 419 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0)) 420 aint = AD_DATE_START; 421 memcpy(data, &aint, sizeof( aint )); 422 data += sizeof( aint ); 423 break; 424 425 case VOLPBIT_VID : 426 memcpy(data, &vol->v_vid, sizeof( vol->v_vid )); 427 data += sizeof( vol->v_vid ); 428 break; 429 430 case VOLPBIT_BFREE : 431 bfree = htonl( bfree ); 432 memcpy(data, &bfree, sizeof( bfree )); 433 data += sizeof( bfree ); 434 break; 435 436 case VOLPBIT_BTOTAL : 437 btotal = htonl( btotal ); 438 memcpy(data, &btotal, sizeof( btotal )); 439 data += sizeof( btotal ); 440 break; 441 442#ifndef NO_LARGE_VOL_SUPPORT 443 case VOLPBIT_XBFREE : 444 xbfree = hton64( xbfree ); 445 memcpy(data, &xbfree, sizeof( xbfree )); 446 data += sizeof( xbfree ); 447 break; 448 449 case VOLPBIT_XBTOTAL : 450 xbtotal = hton64( xbtotal ); 451 memcpy(data, &xbtotal, sizeof( xbtotal )); 452 data += sizeof( xbfree ); 453 break; 454#endif /* ! NO_LARGE_VOL_SUPPORT */ 455 456 case VOLPBIT_NAME : 457 nameoff = data; 458 data += sizeof( uint16_t ); 459 break; 460 461 case VOLPBIT_BSIZE: /* block size */ 462 bsize = htonl(bsize); 463 memcpy(data, &bsize, sizeof(bsize)); 464 data += sizeof(bsize); 465 break; 466 467 default : 468 if ( isad ) { 469 ad_close( &ad, ADFLAGS_HF ); 470 } 471 return( AFPERR_BITMAP ); 472 } 473 bitmap = bitmap>>1; 474 bit++; 475 } 476 if ( nameoff ) { 477 ashort = htons( data - buf ); 478 memcpy(nameoff, &ashort, sizeof( ashort )); 479 /* name is always in mac charset */ 480 aint = ucs2_to_charset( vol->v_maccharset, vol->v_macname, data+1, AFPVOL_MACNAMELEN + 1); 481 if ( aint <= 0 ) { 482 *buflen = 0; 483 return AFPERR_MISC; 484 } 485 486 *data++ = aint; 487 data += aint; 488 } 489 if ( isad ) { 490 ad_close(&ad, ADFLAGS_HF); 491 } 492 *buflen = data - buf; 493 return( AFP_OK ); 494} 495 496/* ------------------------- */ 497static int stat_vol(const AFPObj *obj, uint16_t bitmap, struct vol *vol, char *rbuf, size_t *rbuflen) 498{ 499 struct stat st; 500 int ret; 501 size_t buflen; 502 503 if ( stat( vol->v_path, &st ) < 0 ) { 504 *rbuflen = 0; 505 return( AFPERR_PARAM ); 506 } 507 /* save the volume device number */ 508 vol->v_dev = st.st_dev; 509 510 buflen = *rbuflen - sizeof( bitmap ); 511 if (( ret = getvolparams(obj, bitmap, vol, &st, 512 rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) { 513 *rbuflen = 0; 514 return( ret ); 515 } 516 *rbuflen = buflen + sizeof( bitmap ); 517 bitmap = htons( bitmap ); 518 memcpy(rbuf, &bitmap, sizeof( bitmap )); 519 return( AFP_OK ); 520 521} 522 523/* ------------------------------- */ 524int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) 525{ 526 struct timeval tv; 527 struct stat st; 528 struct vol *volume; 529 char *data; 530 char *namebuf; 531 int vcnt; 532 size_t len; 533 uint32_t aint; 534 535 load_volumes(obj); 536 537 data = rbuf + 5; 538 for ( vcnt = 0, volume = getvolumes(); volume; volume = volume->v_next ) { 539 if (!(volume->v_flags & AFPVOL_NOSTAT)) { 540 struct maccess ma; 541 542 if ( stat( volume->v_path, &st ) < 0 ) { 543 LOG(log_info, logtype_afpd, "afp_getsrvrparms(%s): stat: %s", 544 volume->v_path, strerror(errno) ); 545 continue; /* can't access directory */ 546 } 547 if (!S_ISDIR(st.st_mode)) { 548 continue; /* not a dir */ 549 } 550 accessmode(obj, volume, volume->v_path, &ma, NULL, &st); 551 if ((ma.ma_user & (AR_UREAD | AR_USEARCH)) != (AR_UREAD | AR_USEARCH)) { 552 continue; /* no r-x access */ 553 } 554 } 555 556 if (utf8_encoding(obj)) { 557 len = ucs2_to_charset_allocate(CH_UTF8_MAC, &namebuf, volume->v_u8mname); 558 } else { 559 len = ucs2_to_charset_allocate(obj->options.maccharset, &namebuf, volume->v_macname); 560 } 561 562 if (len == (size_t)-1) 563 continue; 564 565 /* set password bit if there's a volume password */ 566 *data = (volume->v_password) ? AFPSRVR_PASSWD : 0; 567 568 *data++ |= 0; /* UNIX PRIVS BIT ..., OSX doesn't seem to use it, so we don't either */ 569 *data++ = len; 570 memcpy(data, namebuf, len ); 571 data += len; 572 free(namebuf); 573 vcnt++; 574 } 575 576 *rbuflen = data - rbuf; 577 data = rbuf; 578 if ( gettimeofday( &tv, NULL ) < 0 ) { 579 LOG(log_error, logtype_afpd, "afp_getsrvrparms: gettimeofday: %s", strerror(errno) ); 580 *rbuflen = 0; 581 return AFPERR_PARAM; 582 } 583 584 aint = AD_DATE_FROM_UNIX(tv.tv_sec); 585 memcpy(data, &aint, sizeof( uint32_t)); 586 data += sizeof( uint32_t); 587 *data = vcnt; 588 return( AFP_OK ); 589} 590 591/* ------------------------- */ 592static int volume_codepage(AFPObj *obj, struct vol *volume) 593{ 594 struct charset_functions *charset; 595 /* Codepages */ 596 597 if (!volume->v_volcodepage) 598 volume->v_volcodepage = strdup("UTF8"); 599 600 if ( (charset_t) -1 == ( volume->v_volcharset = add_charset(volume->v_volcodepage)) ) { 601 LOG (log_error, logtype_afpd, "Setting codepage %s as volume codepage failed", volume->v_volcodepage); 602 return -1; 603 } 604 605 if ( NULL == (charset = find_charset_functions(volume->v_volcodepage)) || charset->flags & CHARSET_ICONV ) { 606 LOG (log_warning, logtype_afpd, "WARNING: volume encoding %s is *not* supported by netatalk, expect problems !!!!", volume->v_volcodepage); 607 } 608 609 if (!volume->v_maccodepage) 610 volume->v_maccodepage = strdup(obj->options.maccodepage); 611 612 if ( (charset_t) -1 == ( volume->v_maccharset = add_charset(volume->v_maccodepage)) ) { 613 LOG (log_error, logtype_afpd, "Setting codepage %s as mac codepage failed", volume->v_maccodepage); 614 return -1; 615 } 616 617 if ( NULL == ( charset = find_charset_functions(volume->v_maccodepage)) || ! (charset->flags & CHARSET_CLIENT) ) { 618 LOG (log_error, logtype_afpd, "Fatal error: mac charset %s not supported", volume->v_maccodepage); 619 return -1; 620 } 621 volume->v_kTextEncoding = htonl(charset->kTextEncoding); 622 return 0; 623} 624 625/* ------------------------- */ 626static int volume_openDB(const AFPObj *obj, struct vol *volume) 627{ 628 int flags = 0; 629 630 if ((volume->v_flags & AFPVOL_NODEV)) { 631 flags |= CNID_FLAG_NODEV; 632 } 633 634 LOG(log_debug, logtype_afpd, "CNID server: %s:%s", volume->v_cnidserver, volume->v_cnidport); 635 636 volume->v_cdb = cnid_open(volume->v_path, 637 volume->v_umask, 638 volume->v_cnidscheme, 639 flags, 640 volume->v_cnidserver, 641 volume->v_cnidport); 642 643 if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) { 644 /* The first attempt failed and it wasn't yet an attempt to open in-memory */ 645 LOG(log_error, logtype_afpd, "Can't open volume \"%s\" CNID backend \"%s\" ", 646 volume->v_path, volume->v_cnidscheme); 647 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.", 648 volume->v_path); 649 flags |= CNID_FLAG_MEMORY; 650 volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL); 651#ifdef SERVERTEXT 652 /* kill ourself with SIGUSR2 aka msg pending */ 653 if (volume->v_cdb) { 654 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead." 655 "Check server messages for details!"); 656 kill(getpid(), SIGUSR2); 657 /* deactivate cnid caching/storing in AppleDouble files */ 658 } 659#endif 660 } 661 662 return (!volume->v_cdb)?-1:0; 663} 664 665/* 666 * Send list of open volumes to afpd master via IPC 667 */ 668static void server_ipc_volumes(AFPObj *obj) 669{ 670 struct vol *volume, *vols; 671 volume = vols = getvolumes(); 672 bstring openvolnames = bfromcstr(""); 673 bool firstvol = true; 674 675 while (volume) { 676 if (volume->v_flags & AFPVOL_OPEN) { 677 if (!firstvol) 678 bcatcstr(openvolnames, ", "); 679 else 680 firstvol = false; 681 bcatcstr(openvolnames, volume->v_localname); 682 } 683 volume = volume->v_next; 684 } 685 686 ipc_child_write(obj->ipc_fd, IPC_VOLUMES, blength(openvolnames), bdata(openvolnames)); 687 bdestroy(openvolnames); 688} 689 690/* ------------------------- 691 * we are the user here 692 */ 693int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) 694{ 695 struct stat st; 696 char *volname; 697 698 struct vol *volume; 699 struct dir *dir; 700 int len, ret; 701 size_t namelen; 702 uint16_t bitmap; 703 char *vol_uname; 704 char *vol_mname = NULL; 705 char *volname_tmp; 706 707 ibuf += 2; 708 memcpy(&bitmap, ibuf, sizeof( bitmap )); 709 bitmap = ntohs( bitmap ); 710 ibuf += sizeof( bitmap ); 711 712 *rbuflen = 0; 713 if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) { 714 return AFPERR_BITMAP; 715 } 716 717 len = (unsigned char)*ibuf++; 718 volname = obj->oldtmp; 719 720 if ((volname_tmp = strchr(volname,'+')) != NULL) 721 volname = volname_tmp+1; 722 723 if (utf8_encoding(obj)) { 724 namelen = convert_string(CH_UTF8_MAC, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp)); 725 } else { 726 namelen = convert_string(obj->options.maccharset, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp)); 727 } 728 729 if ( namelen <= 0) { 730 return AFPERR_PARAM; 731 } 732 733 ibuf += len; 734 if ((len + 1) & 1) /* pad to an even boundary */ 735 ibuf++; 736 737 load_volumes(obj); 738 739 for ( volume = getvolumes(); volume; volume = volume->v_next ) { 740 if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) { 741 break; 742 } 743 } 744 745 if ( volume == NULL ) { 746 return AFPERR_PARAM; 747 } 748 749 /* check for a volume password */ 750 if (volume->v_password && strncmp(ibuf, volume->v_password, VOLPASSLEN)) { 751 return AFPERR_ACCESS; 752 } 753 754 if (( volume->v_flags & AFPVOL_OPEN ) ) { 755 /* the volume is already open */ 756 return stat_vol(obj, bitmap, volume, rbuf, rbuflen); 757 } 758 759 if (volume->v_root_preexec) { 760 if ((ret = afprun(1, volume->v_root_preexec, NULL)) && volume->v_root_preexec_close) { 761 LOG(log_error, logtype_afpd, "afp_openvol(%s): root preexec : %d", volume->v_path, ret ); 762 return AFPERR_MISC; 763 } 764 } 765 766 if (volume->v_preexec) { 767 if ((ret = afprun(0, volume->v_preexec, NULL)) && volume->v_preexec_close) { 768 LOG(log_error, logtype_afpd, "afp_openvol(%s): preexec : %d", volume->v_path, ret ); 769 return AFPERR_MISC; 770 } 771 } 772 773 if ( stat( volume->v_path, &st ) < 0 ) { 774 return AFPERR_PARAM; 775 } 776 777 if ( chdir( volume->v_path ) < 0 ) { 778 return AFPERR_PARAM; 779 } 780 781 if (volume_codepage(obj, volume) < 0) { 782 ret = AFPERR_MISC; 783 goto openvol_err; 784 } 785 786 /* initialize volume variables 787 * FIXME file size 788 */ 789 if (utf8_encoding(obj)) { 790 volume->max_filename = UTF8FILELEN_EARLY; 791 } 792 else { 793 volume->max_filename = MACFILELEN; 794 } 795 796 volume->v_flags |= AFPVOL_OPEN; 797 volume->v_cdb = NULL; 798 799 if (utf8_encoding(obj)) { 800 len = convert_string_allocate(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, namelen, &vol_mname); 801 } else { 802 len = convert_string_allocate(CH_UCS2, obj->options.maccharset, volume->v_macname, namelen, &vol_mname); 803 } 804 if ( !vol_mname || len <= 0) { 805 ret = AFPERR_MISC; 806 goto openvol_err; 807 } 808 809 if ((vol_uname = strrchr(volume->v_path, '/')) == NULL) 810 vol_uname = volume->v_path; 811 else if (vol_uname[1] != '\0') 812 vol_uname++; 813 814 if ((dir = dir_new(vol_mname, 815 vol_uname, 816 volume, 817 DIRDID_ROOT_PARENT, 818 DIRDID_ROOT, 819 bfromcstr(volume->v_path), 820 &st) 821 ) == NULL) { 822 free(vol_mname); 823 LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) ); 824 ret = AFPERR_MISC; 825 goto openvol_err; 826 } 827 volume->v_root = dir; 828 curdir = dir; 829 830 if (volume_openDB(obj, volume) < 0) { 831 LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s", 832 volume->v_path, volume->v_cnidscheme); 833 ret = AFPERR_MISC; 834 goto openvol_err; 835 } 836 837 ret = stat_vol(obj, bitmap, volume, rbuf, rbuflen); 838 839 if (ret == AFP_OK) { 840 /* 841 * If you mount a volume twice, the second time the trash appears on 842 * the desk-top. That's because the Mac remembers the DID for the 843 * trash (even for volumes in different zones, on different servers). 844 * Just so this works better, we prime the DID cache with the trash, 845 * fixing the trash at DID 17. 846 * FIXME (RL): should it be done inside a CNID backend ? (always returning Trash DID when asked) ? 847 */ 848 if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) { 849 850 /* FIXME find db time stamp */ 851 if (cnid_getstamp(volume->v_cdb, volume->v_stamp, sizeof(volume->v_stamp)) < 0) { 852 LOG (log_error, logtype_afpd, 853 "afp_openvol(%s): Fatal error: Unable to get stamp value from CNID backend", 854 volume->v_path); 855 ret = AFPERR_MISC; 856 goto openvol_err; 857 } 858 } 859 860 const char *msg; 861 if ((msg = atalk_iniparser_getstring(obj->iniconfig, volume->v_configname, "login message", NULL)) != NULL) 862 setmessage(msg); 863 864 free(vol_mname); 865 server_ipc_volumes(obj); 866 return( AFP_OK ); 867 } 868 869openvol_err: 870 if (volume->v_root) { 871 dir_free( volume->v_root ); 872 volume->v_root = NULL; 873 } 874 875 volume->v_flags &= ~AFPVOL_OPEN; 876 if (volume->v_cdb != NULL) { 877 cnid_close(volume->v_cdb); 878 volume->v_cdb = NULL; 879 } 880 free(vol_mname); 881 *rbuflen = 0; 882 return ret; 883} 884 885void closevol(const AFPObj *obj, struct vol *vol) 886{ 887 if (!vol) 888 return; 889 890 vol->v_flags &= ~AFPVOL_OPEN; 891 892 of_closevol(obj, vol); 893 894 dir_free( vol->v_root ); 895 vol->v_root = NULL; 896 if (vol->v_cdb != NULL) { 897 cnid_close(vol->v_cdb); 898 vol->v_cdb = NULL; 899 } 900 901 if (vol->v_postexec) { 902 afprun(0, vol->v_postexec, NULL); 903 } 904 if (vol->v_root_postexec) { 905 afprun(1, vol->v_root_postexec, NULL); 906 } 907} 908 909/* ------------------------- */ 910void close_all_vol(const AFPObj *obj) 911{ 912 struct vol *ovol; 913 curdir = NULL; 914 for ( ovol = getvolumes(); ovol; ovol = ovol->v_next ) { 915 if ( (ovol->v_flags & AFPVOL_OPEN) ) { 916 ovol->v_flags &= ~AFPVOL_OPEN; 917 closevol(obj, ovol); 918 } 919 } 920} 921 922/* ------------------------- */ 923int afp_closevol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) 924{ 925 struct vol *vol; 926 uint16_t vid; 927 928 *rbuflen = 0; 929 ibuf += 2; 930 memcpy(&vid, ibuf, sizeof( vid )); 931 if (NULL == ( vol = getvolbyvid( vid )) ) { 932 return( AFPERR_PARAM ); 933 } 934 935 (void)chdir("/"); 936 curdir = NULL; 937 closevol(obj, vol); 938 server_ipc_volumes(obj); 939 940 return( AFP_OK ); 941} 942 943/* -------------------------- 944 poll if a volume is changed by other processes. 945 return 946 0 no attention msg sent 947 1 attention msg sent 948 -1 error (socket closed) 949 950 Note: if attention return -1 no packet has been 951 sent because the buffer is full, we don't care 952 either there's no reader or there's a lot of 953 traffic and another pollvoltime will follow 954*/ 955int pollvoltime(AFPObj *obj) 956 957{ 958 struct vol *vol; 959 struct timeval tv; 960 struct stat st; 961 962 if (!(obj->afp_version > 21 && obj->options.flags & OPTION_SERVERNOTIF)) 963 return 0; 964 965 if ( gettimeofday( &tv, NULL ) < 0 ) 966 return 0; 967 968 for ( vol = getvolumes(); vol; vol = vol->v_next ) { 969 if ( (vol->v_flags & AFPVOL_OPEN) && vol->v_mtime + 30 < tv.tv_sec) { 970 if ( !stat( vol->v_path, &st ) && vol->v_mtime != st.st_mtime ) { 971 vol->v_mtime = st.st_mtime; 972 if (!obj->attention(obj->dsi, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED)) 973 return -1; 974 return 1; 975 } 976 } 977 } 978 return 0; 979} 980 981/* ------------------------- */ 982void setvoltime(AFPObj *obj, struct vol *vol) 983{ 984 struct timeval tv; 985 986 if ( gettimeofday( &tv, NULL ) < 0 ) { 987 LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) ); 988 return; 989 } 990 if( utime( vol->v_path, NULL ) < 0 ) { 991 /* write of time failed ... probably a read only filesys, 992 * where no other users can interfere, so there's no issue here 993 */ 994 } 995 996 /* a little granularity */ 997 if (vol->v_mtime < tv.tv_sec) { 998 vol->v_mtime = tv.tv_sec; 999 /* or finder doesn't update free space 1000 * AFP 3.2 and above clients seem to be ok without so many notification 1001 */ 1002 if (obj->afp_version < 32 && obj->options.flags & OPTION_SERVERNOTIF) { 1003 obj->attention(obj->dsi, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED); 1004 } 1005 } 1006} 1007 1008/* ------------------------- */ 1009int afp_getvolparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_,char *rbuf, size_t *rbuflen) 1010{ 1011 struct vol *vol; 1012 uint16_t vid, bitmap; 1013 1014 ibuf += 2; 1015 memcpy(&vid, ibuf, sizeof( vid )); 1016 ibuf += sizeof( vid ); 1017 memcpy(&bitmap, ibuf, sizeof( bitmap )); 1018 bitmap = ntohs( bitmap ); 1019 1020 if (NULL == ( vol = getvolbyvid( vid )) ) { 1021 *rbuflen = 0; 1022 return( AFPERR_PARAM ); 1023 } 1024 1025 return stat_vol(obj, bitmap, vol, rbuf, rbuflen); 1026} 1027 1028/* ------------------------- */ 1029int afp_setvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) 1030{ 1031 struct adouble ad; 1032 struct vol *vol; 1033 uint16_t vid, bitmap; 1034 uint32_t aint; 1035 1036 ibuf += 2; 1037 *rbuflen = 0; 1038 1039 memcpy(&vid, ibuf, sizeof( vid )); 1040 ibuf += sizeof( vid ); 1041 memcpy(&bitmap, ibuf, sizeof( bitmap )); 1042 bitmap = ntohs( bitmap ); 1043 ibuf += sizeof(bitmap); 1044 1045 if (( vol = getvolbyvid( vid )) == NULL ) { 1046 return( AFPERR_PARAM ); 1047 } 1048 1049 if ((vol->v_flags & AFPVOL_RO)) 1050 return AFPERR_VLOCK; 1051 1052 /* we can only set the backup date. */ 1053 if (bitmap != (1 << VOLPBIT_BDATE)) 1054 return AFPERR_BITMAP; 1055 1056 ad_init(&ad, vol); 1057 if ( ad_open(&ad, vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR) < 0 ) { 1058 if (errno == EROFS) 1059 return AFPERR_VLOCK; 1060 1061 return AFPERR_ACCESS; 1062 } 1063 1064 memcpy(&aint, ibuf, sizeof(aint)); 1065 ad_setdate(&ad, AD_DATE_BACKUP, aint); 1066 ad_flush(&ad); 1067 ad_close(&ad, ADFLAGS_HF); 1068 return( AFP_OK ); 1069} 1070