• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.0/libatalk/util/
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