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