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