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