1/* 2 This program is free software; you can redistribute it and/or modify 3 it under the terms of the GNU General Public License as published by 4 the Free Software Foundation; either version 2 of the License, or 5 (at your option) any later version. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 GNU General Public License for more details. 11 12 You should have received a copy of the GNU General Public License 13 along with this program; if not, write to the Free Software 14 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 15 16 .volinfo file handling, command line utilities 17 copyright Bjoern Fernhomberg, 2004 18*/ 19 20#ifdef HAVE_CONFIG_H 21#include "config.h" 22#endif /* HAVE_CONFIG_H */ 23 24#include <unistd.h> 25#include <sys/types.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <ctype.h> 29#include <errno.h> 30#include <fcntl.h> 31#ifdef HAVE_STRINGS_H 32#include <strings.h> 33#endif 34#ifdef STDC_HEADERS 35#include <string.h> 36#endif 37#include <sys/param.h> 38 39#include <atalk/adouble.h> 40#include <atalk/util.h> 41#include <atalk/logger.h> 42#include <atalk/volinfo.h> 43#include <atalk/volume.h> 44#ifdef CNID_DB 45#include <atalk/cnid.h> 46#endif /* CNID_DB*/ 47 48static const vol_opt_name_t vol_opt_names[] = { 49 {AFPVOL_A2VOL, "PRODOS"}, /* prodos volume */ 50 {AFPVOL_CRLF, "CRLF"}, /* cr/lf translation */ 51 {AFPVOL_NOADOUBLE, "NOADOUBLE"}, /* don't create .AppleDouble by default */ 52 {AFPVOL_RO, "READONLY"}, /* read-only volume */ 53 {AFPVOL_MSWINDOWS, "MSWINDOWS"}, /* deal with ms-windows yuckiness. this is going away. */ 54 {AFPVOL_NOHEX, "NOHEX"}, /* don't do :hex translation */ 55 {AFPVOL_USEDOTS, "USEDOTS"}, /* use real dots */ 56 {AFPVOL_LIMITSIZE, "LIMITSIZE"}, /* limit size for older macs */ 57 {AFPVOL_MAPASCII, "MAPASCII"}, /* map the ascii range as well */ 58 {AFPVOL_DROPBOX, "DROPBOX"}, /* dropkludge dropbox support */ 59 {AFPVOL_NOFILEID, "NOFILEID"}, /* don't advertise createid resolveid and deleteid calls */ 60 {AFPVOL_NOSTAT, "NOSTAT"}, /* advertise the volume even if we can't stat() it 61 * maybe because it will be mounted later in preexec */ 62 {AFPVOL_UNIX_PRIV, "UNIXPRIV"}, /* support unix privileges */ 63 {AFPVOL_NODEV, "NODEV"}, /* always use 0 for device number in cnid calls */ 64 {AFPVOL_CASEINSEN, "CASEINSENSITIVE"}, /* volume is case insensitive */ 65 {AFPVOL_EILSEQ, "ILLEGALSEQ"}, /* encode illegal sequence */ 66 {AFPVOL_CACHE, "CACHEID"}, /* Use adouble v2 CNID caching, default don't use it */ 67 {AFPVOL_INV_DOTS, "INVISIBLEDOTS"}, 68 {AFPVOL_ACLS, "ACLS"}, /* Vol supports ACLs */ 69 {AFPVOL_TM, "TM"}, /* Set "kSupportsTMLockSteal" is volume attributes */ 70 {0, NULL} 71}; 72 73static const vol_opt_name_t vol_opt_casefold[] = { 74 {AFPVOL_MTOUUPPER, "MTOULOWER"}, 75 {AFPVOL_MTOULOWER, "MTOULOWER"}, 76 {AFPVOL_UTOMUPPER, "UTOMUPPER"}, 77 {AFPVOL_UTOMLOWER, "UTOMLOWER"}, 78 {0, NULL} 79}; 80 81typedef struct { 82 const char *name; 83 int type; 84} info_option_t; 85 86#define MAC_CHARSET 0 87#define VOL_CHARSET 1 88#define ADOUBLE_VER 2 89#define CNIDBACKEND 3 90#define CNIDDBDHOST 4 91#define CNIDDBDPORT 5 92#define CNID_DBPATH 6 93#define VOLUME_OPTS 7 94#define VOLCASEFOLD 8 95#define EXTATTRTYPE 9 96 97static const info_option_t info_options[] = { 98 {"MAC_CHARSET", MAC_CHARSET}, 99 {"VOL_CHARSET", VOL_CHARSET}, 100 {"ADOUBLE_VER", ADOUBLE_VER}, 101 {"CNIDBACKEND", CNIDBACKEND}, 102 {"CNIDDBDHOST", CNIDDBDHOST}, 103 {"CNIDDBDPORT", CNIDDBDPORT}, 104 {"CNID_DBPATH", CNID_DBPATH}, 105 {"VOLUME_OPTS", VOLUME_OPTS}, 106 {"VOLCASEFOLD", VOLCASEFOLD}, 107 {"EXTATTRTYPE", EXTATTRTYPE}, 108 {NULL, 0} 109}; 110 111static char* find_in_path( char *path, char *subdir, size_t maxlen) 112{ 113 char *pos; 114 struct stat st; 115 116 strlcat(path, subdir, maxlen); 117 pos = strrchr(path, '/'); 118 119 while ( stat(path, &st) != 0) { 120 path[pos-path]=0; 121 if ((pos = strrchr(path, '/'))) { 122 path[pos-path]=0; 123 strlcat(path, subdir, maxlen); 124 } 125 else { 126 return NULL; 127 } 128 } 129 130 path[pos-path] = '/'; 131 path[pos-path+1] = 0; 132 133 return path; 134} 135 136static char * make_path_absolute(char *path, size_t bufsize) 137{ 138 struct stat st; 139 char savecwd[MAXPATHLEN]; 140 char abspath[MAXPATHLEN]; 141 char *p; 142 143 strlcpy(abspath, path, sizeof(abspath)); 144 145 /* we might be called from `ad cp ...` with non existing target */ 146 if (stat(abspath, &st) != 0) { 147 if (errno != ENOENT) 148 return NULL; 149 150 if (NULL == (p = strrchr(abspath, '/')) ) 151 /* single component `ad cp SOURCEFILE TARGETFILE`, use "." instead */ 152 strcpy(abspath, "."); 153 else 154 /* try without the last path element */ 155 *p = '\0'; 156 157 if (stat(abspath, &st) != 0) { 158 return NULL; 159 } 160 } 161 162 if (S_ISREG(st.st_mode)) { 163 /* single file copy SOURCE */ 164 if (NULL == (p = strrchr(abspath, '/')) ) 165 /* no path, use "." instead */ 166 strcpy(abspath, "."); 167 else 168 /* try without the last path element */ 169 *p = '\0'; 170 } 171 172 if (!getcwd(savecwd, sizeof(savecwd)) || chdir(abspath) < 0) 173 return NULL; 174 175 if (!getcwd(abspath, sizeof(abspath)) || chdir (savecwd) < 0) 176 return NULL; 177 178 if (strlen(abspath) > bufsize) 179 return NULL; 180 181 strlcpy(path, abspath, bufsize); 182 return path; 183} 184 185static char * find_volumeroot(char *path, size_t maxlen) 186{ 187 char *volume = make_path_absolute(path, maxlen); 188 189 if (volume == NULL) 190 return NULL; 191 192 if (NULL == (find_in_path(volume, "/.AppleDesktop", maxlen)) ) 193 return NULL; 194 195 return volume; 196} 197 198int vol_load_charsets( struct volinfo *vol) 199{ 200 if ( (charset_t) -1 == ( vol->v_maccharset = add_charset(vol->v_maccodepage)) ) { 201 fprintf( stderr, "Setting codepage %s as Mac codepage failed", vol->v_maccodepage); 202 return (-1); 203 } 204 205 if ( (charset_t) -1 == ( vol->v_volcharset = add_charset(vol->v_volcodepage)) ) { 206 fprintf( stderr, "Setting codepage %s as volume codepage failed", vol->v_volcodepage); 207 return (-1); 208 } 209 210 return 0; 211} 212 213static int parse_options (char *buf, int *flags, const vol_opt_name_t *options) 214{ 215 char *p, *q; 216 const vol_opt_name_t *op; 217 218 q = p = buf; 219 220 while ( *p != '\0') { 221 if (*p == ' ') { 222 *p = '\0'; 223 op = options; 224 for (;op->name; op++) { 225 if ( !strcmp(op->name, q )) { 226 *flags |= op->option; 227 break; 228 } 229 } 230 q = p+1; 231 } 232 p++; 233 } 234 235 return 0; 236} 237 238 239 240static int parseline ( char *buf, struct volinfo *vol) 241{ 242 char *value; 243 size_t len; 244 int option=-1; 245 const info_option_t *p = &info_options[0]; 246 247 if (NULL == ( value = strchr(buf, ':')) ) 248 return 1; 249 250 *value = '\0'; 251 value++; 252 253 if ( 0 == (len = strlen(value)) ) 254 return 0; 255 256 if (value[len-1] == '\n') 257 value[len-1] = '\0'; 258 259 for (;p->name; p++) { 260 if ( !strcmp(p->name, buf )) { 261 option=p->type; 262 break; 263 } 264 } 265 266 switch (option) { 267 case MAC_CHARSET: 268 if ((vol->v_maccodepage = strdup(value)) == NULL) { 269 fprintf (stderr, "strdup: %s", strerror(errno)); 270 return -1; 271 } 272 break; 273 case VOL_CHARSET: 274 if ((vol->v_volcodepage = strdup(value)) == NULL) { 275 fprintf (stderr, "strdup: %s", strerror(errno)); 276 return -1; 277 } 278 break; 279 case CNIDBACKEND: 280 if ((vol->v_cnidscheme = strdup(value)) == NULL) { 281 fprintf (stderr, "strdup: %s", strerror(errno)); 282 return -1; 283 } 284 break; 285 case CNIDDBDHOST: 286 if ((vol->v_dbd_host = strdup(value)) == NULL) { 287 fprintf (stderr, "strdup: %s", strerror(errno)); 288 return -1; 289 } 290 break; 291 case CNIDDBDPORT: 292 if ((vol->v_dbd_port = strdup(value)) == NULL) { 293 fprintf (stderr, "strdup: %s", strerror(errno)); 294 return -1; 295 } 296 break; 297 case CNID_DBPATH: 298 if ((vol->v_dbpath = malloc(MAXPATHLEN+1)) == NULL) 299 return -1; 300 strcpy(vol->v_dbpath, value); 301 break; 302 case ADOUBLE_VER: 303 if (strcasecmp(value, "v1") == 0) { 304 vol->v_adouble = AD_VERSION1; 305 vol->ad_path = ad_path; 306 } 307#if AD_VERSION == AD_VERSION2 308 else if (strcasecmp(value, "v2") == 0) { 309 vol->ad_path = ad_path; 310 vol->v_adouble = AD_VERSION2; 311 } 312 else if (strcasecmp(value, "osx") == 0) { 313 vol->v_adouble = AD_VERSION2_OSX; 314 vol->ad_path = ad_path_osx; 315 } 316#endif 317 else { 318 fprintf (stderr, "unknown adouble version: %s, %s", buf, value); 319 return -1; 320 } 321 break; 322 case VOLUME_OPTS: 323 parse_options(value, &vol->v_flags, &vol_opt_names[0]); 324 break; 325 case VOLCASEFOLD: 326 parse_options(value, &vol->v_casefold, &vol_opt_casefold[0]); 327 break; 328 case EXTATTRTYPE: 329 if (strcasecmp(value, "AFPVOL_EA_AD") == 0) 330 vol->v_vfs_ea = AFPVOL_EA_AD; 331 else if (strcasecmp(value, "AFPVOL_EA_SYS") == 0) 332 vol->v_vfs_ea = AFPVOL_EA_SYS; 333 break; 334 default: 335 fprintf (stderr, "unknown volume information: %s, %s", buf, value); 336 return (-1); 337 break; 338 } 339 340 return 0; 341} 342 343 344int loadvolinfo (char *path, struct volinfo *vol) 345{ 346 347 char volinfofile[MAXPATHLEN]; 348 char buf[MAXPATHLEN]; 349 struct flock lock; 350 int fd, len; 351 FILE *fp; 352 353 if ( !path || !vol) 354 return -1; 355 356 memset(vol, 0, sizeof(struct volinfo)); 357 strlcpy(volinfofile, path, sizeof(volinfofile)); 358 359 /* volinfo file is in .AppleDesktop */ 360 if ( NULL == find_volumeroot(volinfofile, sizeof(volinfofile))) 361 return -1; 362 363 if ((vol->v_path = strdup(volinfofile)) == NULL ) { 364 fprintf (stderr, "strdup: %s", strerror(errno)); 365 return (-1); 366 } 367 /* Remove trailing slashes */ 368 len = strlen(vol->v_path); 369 while (len && (vol->v_path[len-1] == '/')) { 370 vol->v_path[len-1] = 0; 371 len--; 372 } 373 374 strlcat(volinfofile, ".AppleDesktop/", sizeof(volinfofile)); 375 strlcat(volinfofile, VOLINFOFILE, sizeof(volinfofile)); 376 377 /* open the file read only */ 378 if ( NULL == (fp = fopen( volinfofile, "r")) ) { 379 fprintf (stderr, "error opening volinfo (%s): %s", volinfofile, strerror(errno)); 380 return (-1); 381 } 382 fd = fileno(fp); 383 384 /* try to get a read lock */ 385 lock.l_start = 0; 386 lock.l_whence = SEEK_SET; 387 lock.l_len = 0; 388 lock.l_type = F_RDLCK; 389 390 /* wait for read lock */ 391 if (fcntl(fd, F_SETLKW, &lock) < 0) { 392 fclose(fp); 393 return (-1); 394 } 395 396 /* read the file */ 397 while (NULL != fgets(buf, sizeof(buf), fp)) { 398 parseline(buf, vol); 399 } 400 401 /* unlock file */ 402 lock.l_type = F_UNLCK; 403 fcntl(fd, F_SETLK, &lock); 404 405 /* Translate vol options to ad options like afp/volume.c does it */ 406 vol->v_ad_options = 0; 407 if ((vol->v_flags & AFPVOL_NODEV)) 408 vol->v_ad_options |= ADVOL_NODEV; 409 if ((vol->v_flags & AFPVOL_CACHE)) 410 vol->v_ad_options |= ADVOL_CACHE; 411 if ((vol->v_flags & AFPVOL_UNIX_PRIV)) 412 vol->v_ad_options |= ADVOL_UNIXPRIV; 413 if ((vol->v_flags & AFPVOL_INV_DOTS)) 414 vol->v_ad_options |= ADVOL_INVDOTS; 415 416 vol->retaincount = 1; 417 418 fclose(fp); 419 return 0; 420} 421 422/*! 423 * Allocate a struct volinfo object for refcounting usage with retain and close, and 424 * call loadvolinfo with it 425 */ 426struct volinfo *allocvolinfo(char *path) 427{ 428 struct volinfo *p = malloc(sizeof(struct volinfo)); 429 if (p == NULL) 430 return NULL; 431 432 if (loadvolinfo(path, p) == -1) 433 return NULL; 434 435 p->malloced = 1; 436 437 return p; 438} 439 440void retainvolinfo(struct volinfo *vol) 441{ 442 vol->retaincount++; 443} 444 445/*! 446 * Decrement retain count, free resources when retaincount reaches 0 447 */ 448int closevolinfo(struct volinfo *volinfo) 449{ 450 if (volinfo->retaincount <= 0) 451 abort(); 452 453 volinfo->retaincount--; 454 455 if (volinfo->retaincount == 0) { 456 free(volinfo->v_name); volinfo->v_name = NULL; 457 free(volinfo->v_path); volinfo->v_path = NULL; 458 free(volinfo->v_cnidscheme); volinfo->v_cnidscheme = NULL; 459 free(volinfo->v_dbpath); volinfo->v_dbpath = NULL; 460 free(volinfo->v_volcodepage); volinfo->v_volcodepage = NULL; 461 free(volinfo->v_maccodepage); volinfo->v_maccodepage = NULL; 462 free(volinfo->v_dbd_host); volinfo->v_dbd_host = NULL; 463 free(volinfo->v_dbd_port); volinfo->v_dbd_port = NULL; 464 if (volinfo->malloced) { 465 volinfo->malloced = 0; 466 free(volinfo); 467 } 468 } 469 470 return 0; 471} 472 473/* 474 * Save the volume options to a file, used by shell utilities. Writing the file 475 * everytime a volume is opened is unnecessary, but it shouldn't hurt much. 476 */ 477int savevolinfo(const struct vol *vol, const char *Cnid_srv, const char *Cnid_port) 478{ 479 uid_t process_uid; 480 char buf[16348]; 481 char item[MAXPATHLEN]; 482 int fd; 483 int ret = 0; 484 struct flock lock; 485 const vol_opt_name_t *op = &vol_opt_names[0]; 486 const vol_opt_name_t *cf = &vol_opt_casefold[0]; 487 488 strlcpy (item, vol->v_path, sizeof(item)); 489 strlcat (item, "/.AppleDesktop/", sizeof(item)); 490 strlcat (item, VOLINFOFILE, sizeof(item)); 491 492 process_uid = geteuid(); 493 if (process_uid) { 494 if (seteuid(0) == -1) { 495 process_uid = 0; 496 } 497 } 498 499 if ((fd = open(item, O_RDWR | O_CREAT , 0666)) <0 ) { 500 LOG(log_debug, logtype_default,"Error opening %s: %s", item, strerror(errno)); 501 if (process_uid) { 502 if (seteuid(process_uid) == -1) { 503 LOG(log_error, logtype_default, "can't seteuid back %s", strerror(errno)); 504 exit(EXITERR_SYS); 505 } 506 } 507 return (-1); 508 } 509 510 if (process_uid) { 511 if (seteuid(process_uid) == -1) { 512 LOG(log_error, logtype_default, "can't seteuid back %s", strerror(errno)); 513 exit(EXITERR_SYS); 514 } 515 } 516 517 /* try to get a lock */ 518 lock.l_start = 0; 519 lock.l_whence = SEEK_SET; 520 lock.l_len = 0; 521 lock.l_type = F_WRLCK; 522 523 if (fcntl(fd, F_SETLK, &lock) < 0) { 524 close(fd); 525 if (errno == EACCES || errno == EAGAIN) { 526 /* ignore, other process already writing the file */ 527 return 0; 528 } else { 529 LOG(log_error, logtype_default, "savevoloptions: cannot get lock: %s", strerror(errno)); 530 return (-1); 531 } 532 } 533 534 /* write volume options */ 535 snprintf(buf, sizeof(buf), "MAC_CHARSET:%s\n", vol->v_maccodepage); 536 snprintf(item, sizeof(item), "VOL_CHARSET:%s\n", vol->v_volcodepage); 537 strlcat(buf, item, sizeof(buf)); 538 539 switch (vol->v_adouble) { 540 case AD_VERSION1: 541 strlcat(buf, "ADOUBLE_VER:v1\n", sizeof(buf)); 542 break; 543 case AD_VERSION2: 544 strlcat(buf, "ADOUBLE_VER:v2\n", sizeof(buf)); 545 break; 546 case AD_VERSION2_OSX: 547 strlcat(buf, "ADOUBLE_VER:osx\n", sizeof(buf)); 548 break; 549 case AD_VERSION1_SFM: 550 strlcat(buf, "ADOUBLE_VER:sfm\n", sizeof(buf)); 551 break; 552 } 553 554 strlcat(buf, "CNIDBACKEND:", sizeof(buf)); 555 strlcat(buf, vol->v_cnidscheme, sizeof(buf)); 556 strlcat(buf, "\n", sizeof(buf)); 557 558 strlcat(buf, "CNIDDBDHOST:", sizeof(buf)); 559 strlcat(buf, Cnid_srv, sizeof(buf)); 560 strlcat(buf, "\n", sizeof(buf)); 561 562 strlcat(buf, "CNIDDBDPORT:", sizeof(buf)); 563 strlcat(buf, Cnid_port, sizeof(buf)); 564 strlcat(buf, "\n", sizeof(buf)); 565 566 strcpy(item, "CNID_DBPATH:"); 567 if (vol->v_dbpath) 568 strlcat(item, vol->v_dbpath, sizeof(item)); 569 else 570 strlcat(item, vol->v_path, sizeof(item)); 571 strlcat(item, "\n", sizeof(item)); 572 strlcat(buf, item, sizeof(buf)); 573 574 /* volume flags */ 575 strcpy(item, "VOLUME_OPTS:"); 576 for (;op->name; op++) { 577 if ( (vol->v_flags & op->option) ) { 578 strlcat(item, op->name, sizeof(item)); 579 strlcat(item, " ", sizeof(item)); 580 } 581 } 582 strlcat(item, "\n", sizeof(item)); 583 strlcat(buf, item, sizeof(buf)); 584 585 /* casefold flags */ 586 strcpy(item, "VOLCASEFOLD:"); 587 for (;cf->name; cf++) { 588 if ( (vol->v_casefold & cf->option) ) { 589 strlcat(item, cf->name, sizeof(item)); 590 strlcat(item, " ", sizeof(item)); 591 } 592 } 593 strlcat(item, "\n", sizeof(item)); 594 strlcat(buf, item, sizeof(buf)); 595 596 /* ExtendedAttributes */ 597 strcpy(item, "EXTATTRTYPE:"); 598 switch (vol->v_vfs_ea) { 599 case AFPVOL_EA_SYS: 600 strlcat(item, "AFPVOL_EA_SYS\n", sizeof(item)); 601 break; 602 case AFPVOL_EA_AD: 603 strlcat(item, "AFPVOL_EA_AD\n", sizeof(item)); 604 break; 605 case AFPVOL_EA_NONE: 606 strlcat(item, "AFPVOL_EA_NONE\n", sizeof(item)); 607 break; 608 default: 609 strlcat(item, "AFPVOL_EA_UNKNOWN\n", sizeof(item)); 610 } 611 612 strlcat(buf, item, sizeof(buf)); 613 614 if (strlen(buf) >= sizeof(buf)-1) 615 LOG(log_debug, logtype_default, "Error writing .volinfo file: buffer too small, %s", buf); 616 if (write( fd, buf, strlen(buf)) < 0 || ftruncate(fd, strlen(buf)) < 0 ) { 617 LOG(log_debug, logtype_default, "Error writing .volinfo file: %s", strerror(errno)); 618 } 619 620 lock.l_type = F_UNLCK; 621 fcntl(fd, F_SETLK, &lock); 622 close (fd); 623 return ret; 624} 625