]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/volume.c
clustering support: new per volume option cnidserver
[netatalk.git] / etc / afpd / volume.c
1 /*
2  * $Id: volume.c,v 1.122 2010-03-31 09:47:32 franklahm Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <pwd.h>
16 #include <grp.h>
17 #include <utime.h>
18 #include <errno.h>
19 #ifdef HAVE_STRINGS_H
20 #include <strings.h>
21 #endif
22 /* STDC check */
23 #if STDC_HEADERS
24 #include <string.h>
25 #else /* STDC_HEADERS */
26 #ifndef HAVE_STRCHR
27 #define strchr index
28 #define strrchr index
29 #endif /* HAVE_STRCHR */
30 char *strchr (), *strrchr ();
31 #ifndef HAVE_MEMCPY
32 #define memcpy(d,s,n) bcopy ((s), (d), (n))
33 #define memmove(d,s,n) bcopy ((s), (d), (n))
34 #endif /* ! HAVE_MEMCPY */
35 #endif /* STDC_HEADERS */
36 #include <sys/param.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <atalk/asp.h>
41 #include <atalk/dsi.h>
42 #include <atalk/adouble.h>
43 #include <atalk/afp.h>
44 #include <atalk/util.h>
45 #include <atalk/volinfo.h>
46 #include <atalk/logger.h>
47 #include <atalk/vfs.h>
48 #include <atalk/ea.h>
49 #ifdef CNID_DB
50 #include <atalk/cnid.h>
51 #endif /* CNID_DB*/
52
53 #include "globals.h"
54 #include "directory.h"
55 #include "file.h"
56 #include "volume.h"
57 #include "unix.h"
58 #include "mangle.h"
59 #include "fork.h"
60 #include "hash.h"
61
62 extern int afprun(int root, char *cmd, int *outfd);
63
64 #ifndef MIN
65 #define MIN(a, b) ((a) < (b) ? (a) : (b))
66 #endif /* ! MIN */
67
68 #ifndef NO_LARGE_VOL_SUPPORT
69 #if BYTE_ORDER == BIG_ENDIAN
70 #define hton64(x)       (x)
71 #define ntoh64(x)       (x)
72 #else /* BYTE_ORDER == BIG_ENDIAN */
73 #define hton64(x)       ((u_int64_t) (htonl(((x) >> 32) & 0xffffffffLL)) | \
74                          (u_int64_t) ((htonl(x) & 0xffffffffLL) << 32))
75 #define ntoh64(x)       (hton64(x))
76 #endif /* BYTE_ORDER == BIG_ENDIAN */
77 #endif /* ! NO_LARGE_VOL_SUPPORT */
78
79 static struct vol *Volumes = NULL;
80 static u_int16_t    lastvid = 0;
81 static char     *Trash = "\02\024Network Trash Folder";
82
83 static struct extmap    *Extmap = NULL, *Defextmap = NULL;
84 static int              Extmap_cnt;
85 static void             free_extmap(void);
86
87 #define VOLOPT_ALLOW      0  /* user allow list */
88 #define VOLOPT_DENY       1  /* user deny list */
89 #define VOLOPT_RWLIST     2  /* user rw list */
90 #define VOLOPT_ROLIST     3  /* user ro list */
91 #define VOLOPT_PASSWORD   4  /* volume password */
92 #define VOLOPT_CASEFOLD   5  /* character case mangling */
93 #define VOLOPT_FLAGS      6  /* various flags */
94 #define VOLOPT_DBPATH     7  /* path to database */
95 #define VOLOPT_MAPCHARS   8  /* does mtou and utom mappings. syntax:
96                                 m and u can be double-byte hex
97                                 strings if necessary.
98                                 m=u -> map both ways
99                                 m>u -> map m to u
100                                 m<u -> map u to m
101                                 !u  -> make u illegal always
102                                 ~u  -> make u illegal only as the first
103                                 part of a double-byte character.
104                              */
105 #define VOLOPT_VETO          10  /* list of veto filespec */
106 #define VOLOPT_PREEXEC       11  /* preexec command */
107 #define VOLOPT_ROOTPREEXEC   12  /* root preexec command */
108 #define VOLOPT_POSTEXEC      13  /* postexec command */
109 #define VOLOPT_ROOTPOSTEXEC  14  /* root postexec command */
110 #define VOLOPT_ENCODING      15  /* mac encoding (pre OSX)*/
111 #define VOLOPT_MACCHARSET    16
112 #define VOLOPT_CNIDSCHEME    17
113 #define VOLOPT_ADOUBLE       18  /* adouble version */
114
115 #ifdef FORCE_UIDGID
116 #warning UIDGID
117 #include "uid.h"
118
119 #define VOLOPT_FORCEUID      19  /* force uid for username x */
120 #define VOLOPT_FORCEGID      20  /* force gid for group x */
121 #endif /* FORCE_UIDGID */
122
123 #define VOLOPT_UMASK         21
124 #define VOLOPT_ALLOWED_HOSTS 22
125 #define VOLOPT_DENIED_HOSTS  23
126 #define VOLOPT_DPERM         24  /* dperm default directories perms */
127 #define VOLOPT_FPERM         25  /* fperm default files perms */
128 #define VOLOPT_DFLTPERM      26  /* perm */
129 #define VOLOPT_EA_VFS        27  /* Extended Attributes vfs indirection */
130 #define VOLOPT_CNIDSERVER    28  /* CNID Server ip address*/
131 #define VOLOPT_CNIDPORT      30  /* CNID server tcp port */
132
133 #define VOLOPT_MAX           31  /* <== IMPORTANT !!!!!! */
134 #define VOLOPT_NUM           (VOLOPT_MAX + 1)
135
136 #define VOLPASSLEN  8
137 #define VOLOPT_DEFAULT     ":DEFAULT:"
138 #define VOLOPT_DEFAULT_LEN 9
139
140 struct vol_option {
141     char *c_value;
142     int i_value;
143 };
144
145 typedef struct _special_folder {
146     const char *name;
147     int precreate;
148     mode_t mode;
149     int hide;
150 } _special_folder;
151
152 static const _special_folder special_folders[] = {
153     {"Network Trash Folder",     1,  0777,  1},
154     {"Temporary Items",          1,  0777,  1},
155     {".AppleDesktop",            1,  0777,  0},
156 #if 0
157     {"TheFindByContentFolder",   0,     0,  1},
158     {"TheVolumeSettingsFolder",  0,     0,  1},
159 #endif
160     {NULL, 0, 0, 0}};
161
162 /* Forward declarations */
163 static void handle_special_folders (const struct vol *);
164 static void deletevol(struct vol *vol);
165 static void volume_free(struct vol *vol);
166 static void check_ea_sys_support(struct vol *vol);
167
168 static void volfree(struct vol_option *options, const struct vol_option *save)
169 {
170     int i;
171
172     if (save) {
173         for (i = 0; i < VOLOPT_MAX; i++) {
174             if (options[i].c_value && (options[i].c_value != save[i].c_value))
175                 free(options[i].c_value);
176         }
177     } else {
178         for (i = 0; i < VOLOPT_MAX; i++) {
179             if (options[i].c_value)
180                 free(options[i].c_value);
181         }
182     }
183 }
184
185
186 /* handle variable substitutions. here's what we understand:
187  * $b   -> basename of path
188  * $c   -> client ip/appletalk address
189  * $d   -> volume pathname on server
190  * $f   -> full name (whatever's in the gecos field)
191  * $g   -> group
192  * $h   -> hostname
193  * $i   -> client ip/appletalk address without port
194  * $s   -> server name (hostname if it doesn't exist)
195  * $u   -> username (guest is usually nobody)
196  * $v   -> volume name or basename if null
197  * $z   -> zone (may not exist)
198  * $$   -> $
199  *
200  *
201  */
202 #define is_var(a, b) (strncmp((a), (b), 2) == 0)
203
204 static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
205                       char *src, struct passwd *pwd, char *path, char *volname)
206 {
207     char *p, *r;
208     const char *q;
209     int len;
210     char *ret;
211
212     if (!src) {
213         return NULL;
214     }
215     if (!dest) {
216         dest = calloc(destlen +1, 1);
217     }
218     ret = dest;
219     if (!ret) {
220         return NULL;
221     }
222     strlcpy(dest, src, destlen +1);
223     if ((p = strchr(src, '$')) == NULL) /* nothing to do */
224         return ret;
225
226     /* first part of the path. just forward to the next variable. */
227     len = MIN((size_t)(p - src), destlen);
228     if (len > 0) {
229         destlen -= len;
230         dest += len;
231     }
232
233     while (p && destlen > 0) {
234         /* now figure out what the variable is */
235         q = NULL;
236         if (is_var(p, "$b")) {
237             if (path) {
238                 if ((q = strrchr(path, '/')) == NULL)
239                     q = path;
240                 else if (*(q + 1) != '\0')
241                     q++;
242             }
243         } else if (is_var(p, "$c")) {
244             if (obj->proto == AFPPROTO_ASP) {
245                 ASP asp = obj->handle;
246
247                 len = sprintf(dest, "%u.%u", ntohs(asp->asp_sat.sat_addr.s_net),
248                               asp->asp_sat.sat_addr.s_node);
249                 dest += len;
250                 destlen -= len;
251
252             } else if (obj->proto == AFPPROTO_DSI) {
253                 DSI *dsi = obj->handle;
254                 len = sprintf(dest, "%s:%u",
255                               getip_string((struct sockaddr *)&dsi->client),
256                               getip_port((struct sockaddr *)&dsi->client));
257                 dest += len;
258                 destlen -= len;
259             }
260         } else if (is_var(p, "$d")) {
261             q = path;
262         } else if (is_var(p, "$f")) {
263             if ((r = strchr(pwd->pw_gecos, ',')))
264                 *r = '\0';
265             q = pwd->pw_gecos;
266         } else if (is_var(p, "$g")) {
267             struct group *grp = getgrgid(pwd->pw_gid);
268             if (grp)
269                 q = grp->gr_name;
270         } else if (is_var(p, "$h")) {
271             q = obj->options.hostname;
272         } else if (is_var(p, "$i")) {
273             if (obj->proto == AFPPROTO_ASP) {
274                 ASP asp = obj->handle;
275
276                 len = sprintf(dest, "%u", ntohs(asp->asp_sat.sat_addr.s_net));
277                 dest += len;
278                 destlen -= len;
279
280             } else if (obj->proto == AFPPROTO_DSI) {
281                 DSI *dsi = obj->handle;
282                 q = getip_string((struct sockaddr *)&dsi->client);
283             }
284         } else if (is_var(p, "$s")) {
285             if (obj->Obj)
286                 q = obj->Obj;
287             else if (obj->options.server) {
288                 q = obj->options.server;
289             } else
290                 q = obj->options.hostname;
291         } else if (is_var(p, "$u")) {
292             char* sep = NULL;
293             if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
294                 q = sep+1;
295             else
296                 q = obj->username;
297         } else if (is_var(p, "$v")) {
298             if (volname) {
299                 q = volname;
300             }
301             else if (path) {
302                 if ((q = strrchr(path, '/')) == NULL)
303                     q = path;
304                 else if (*(q + 1) != '\0')
305                     q++;
306             }
307         } else if (is_var(p, "$z")) {
308             q = obj->Zone;
309         } else if (is_var(p, "$$")) {
310             q = "$";
311         } else
312             q = p;
313
314         /* copy the stuff over. if we don't understand something that we
315          * should, just skip it over. */
316         if (q) {
317             len = MIN(p == q ? 2 : strlen(q), destlen);
318             strncpy(dest, q, len);
319             dest += len;
320             destlen -= len;
321         }
322
323         /* stuff up to next $ */
324         src = p + 2;
325         p = strchr(src, '$');
326         len = p ? MIN((size_t)(p - src), destlen) : destlen;
327         if (len > 0) {
328             strncpy(dest, src, len);
329             dest += len;
330             destlen -= len;
331         }
332     }
333     return ret;
334 }
335
336 /* to make sure that val is valid, make sure to select an opt that
337    includes val */
338 static int optionok(const char *buf, const char *opt, const char *val)
339 {
340     if (!strstr(buf,opt))
341         return 0;
342     if (!val[1])
343         return 0;
344     return 1;
345 }
346
347
348 /* -------------------- */
349 static void setoption(struct vol_option *options, struct vol_option *save, int opt, const char *val)
350 {
351     if (options[opt].c_value && (!save || options[opt].c_value != save[opt].c_value))
352         free(options[opt].c_value);
353     options[opt].c_value = strdup(val + 1);
354 }
355
356 /* ------------------------------------------
357    handle all the options. tmp can't be NULL. */
358 static void volset(struct vol_option *options, struct vol_option *save,
359                    char *volname, int vlen,
360                    const char *tmp)
361 {
362     char *val;
363
364     val = strchr(tmp, ':');
365     if (!val) {
366         /* we'll assume it's a volume name. */
367         strncpy(volname, tmp, vlen);
368         volname[vlen] = 0;
369         return;
370     }
371 #if 0
372     LOG(log_debug, logtype_afpd, "Parsing volset %s", val);
373 #endif
374     if (optionok(tmp, "allow:", val)) {
375         setoption(options, save, VOLOPT_ALLOW, val);
376
377     } else if (optionok(tmp, "deny:", val)) {
378         setoption(options, save, VOLOPT_DENY, val);
379
380     } else if (optionok(tmp, "rwlist:", val)) {
381         setoption(options, save, VOLOPT_RWLIST, val);
382
383     } else if (optionok(tmp, "rolist:", val)) {
384         setoption(options, save, VOLOPT_ROLIST, val);
385
386     } else if (optionok(tmp, "codepage:", val)) {
387         LOG (log_error, logtype_afpd, "The old codepage system has been removed. Please make sure to read the documentation !!!!");
388         /* Make sure we don't screw anything */
389         exit (EXITERR_CONF);
390     } else if (optionok(tmp, "volcharset:", val)) {
391         setoption(options, save, VOLOPT_ENCODING, val);
392     } else if (optionok(tmp, "maccharset:", val)) {
393         setoption(options, save, VOLOPT_MACCHARSET, val);
394     } else if (optionok(tmp, "veto:", val)) {
395         setoption(options, save, VOLOPT_VETO, val);
396     } else if (optionok(tmp, "cnidscheme:", val)) {
397         setoption(options, save, VOLOPT_CNIDSCHEME, val);
398     } else if (optionok(tmp, "casefold:", val)) {
399         if (strcasecmp(val + 1, "tolower") == 0)
400             options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER;
401         else if (strcasecmp(val + 1, "toupper") == 0)
402             options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER;
403         else if (strcasecmp(val + 1, "xlatelower") == 0)
404             options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER;
405         else if (strcasecmp(val + 1, "xlateupper") == 0)
406             options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
407     } else if (optionok(tmp, "adouble:", val)) {
408         if (strcasecmp(val + 1, "v1") == 0)
409             options[VOLOPT_ADOUBLE].i_value = AD_VERSION1;
410 #if AD_VERSION == AD_VERSION2
411         else if (strcasecmp(val + 1, "v2") == 0)
412             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
413         else if (strcasecmp(val + 1, "osx") == 0)
414             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2_OSX;
415         else if (strcasecmp(val + 1, "sfm") == 0)
416             options[VOLOPT_ADOUBLE].i_value = AD_VERSION1_SFM;
417 #endif
418     } else if (optionok(tmp, "options:", val)) {
419         char *p;
420
421         if ((p = strtok(val + 1, ",")) == NULL) /* nothing */
422             return;
423
424         while (p) {
425             if (strcasecmp(p, "prodos") == 0)
426                 options[VOLOPT_FLAGS].i_value |= AFPVOL_A2VOL;
427             else if (strcasecmp(p, "mswindows") == 0) {
428                 options[VOLOPT_FLAGS].i_value |= AFPVOL_MSWINDOWS | AFPVOL_USEDOTS;
429             } else if (strcasecmp(p, "crlf") == 0)
430                 options[VOLOPT_FLAGS].i_value |= AFPVOL_CRLF;
431             else if (strcasecmp(p, "noadouble") == 0)
432                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOADOUBLE;
433             else if (strcasecmp(p, "ro") == 0)
434                 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
435             else if (strcasecmp(p, "nohex") == 0)
436                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX;
437             else if (strcasecmp(p, "usedots") == 0)
438                 options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS;
439             else if (strcasecmp(p, "invisibledots") == 0)
440                 options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS | AFPVOL_INV_DOTS;
441             else if (strcasecmp(p, "limitsize") == 0)
442                 options[VOLOPT_FLAGS].i_value |= AFPVOL_LIMITSIZE;
443             /* support for either "dropbox" or "dropkludge" */
444             else if (strcasecmp(p, "dropbox") == 0)
445                 options[VOLOPT_FLAGS].i_value |= AFPVOL_DROPBOX;
446             else if (strcasecmp(p, "dropkludge") == 0)
447                 options[VOLOPT_FLAGS].i_value |= AFPVOL_DROPBOX;
448             else if (strcasecmp(p, "nofileid") == 0)
449                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOFILEID;
450             else if (strcasecmp(p, "nostat") == 0)
451                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOSTAT;
452             else if (strcasecmp(p, "preexec_close") == 0)
453                 options[VOLOPT_PREEXEC].i_value = 1;
454             else if (strcasecmp(p, "root_preexec_close") == 0)
455                 options[VOLOPT_ROOTPREEXEC].i_value = 1;
456             else if (strcasecmp(p, "upriv") == 0)
457                 options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV;
458             else if (strcasecmp(p, "acls") == 0)
459                 options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
460             else if (strcasecmp(p, "nodev") == 0)
461                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV;
462             else if (strcasecmp(p, "caseinsensitive") == 0)
463                 options[VOLOPT_FLAGS].i_value |= AFPVOL_CASEINSEN;
464             else if (strcasecmp(p, "illegalseq") == 0)
465                 options[VOLOPT_FLAGS].i_value |= AFPVOL_EILSEQ;
466             else if (strcasecmp(p, "nocnidcache") == 0)
467                 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_CACHE;
468             else if (strcasecmp(p, "tm") == 0)
469                 options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
470
471             p = strtok(NULL, ",");
472         }
473
474     } else if (optionok(tmp, "cnidserver:", val)) {
475         setoption(options, save, VOLOPT_CNIDSERVER, val);
476
477         char *p = strrchr(val + 1, ':');
478         if (p) {
479             *p = 0;
480             setoption(options, save, VOLOPT_CNIDPORT, p);
481         }
482
483         LOG(log_debug, logtype_afpd, "CNID Server for volume '%s': %s:%s",
484             volname, val + 1, p ? p + 1 : Cnid_port);
485
486     } else if (optionok(tmp, "dbpath:", val)) {
487         setoption(options, save, VOLOPT_DBPATH, val);
488
489     } else if (optionok(tmp, "umask:", val)) {
490         options[VOLOPT_UMASK].i_value = (int)strtol(val +1, NULL, 8);
491     } else if (optionok(tmp, "dperm:", val)) {
492         options[VOLOPT_DPERM].i_value = (int)strtol(val+1, NULL, 8);
493     } else if (optionok(tmp, "fperm:", val)) {
494         options[VOLOPT_FPERM].i_value = (int)strtol(val+1, NULL, 8);
495     } else if (optionok(tmp, "perm:", val)) {
496         options[VOLOPT_DFLTPERM].i_value = (int)strtol(val+1, NULL, 8);
497     } else if (optionok(tmp, "mapchars:",val)) {
498         setoption(options, save, VOLOPT_MAPCHARS, val);
499
500     } else if (optionok(tmp, "password:", val)) {
501         setoption(options, save, VOLOPT_PASSWORD, val);
502
503 #ifdef FORCE_UIDGID
504
505         /* this code allows forced uid/gid per volume settings */
506     } else if (optionok(tmp, "forceuid:", val)) {
507         setoption(options, save, VOLOPT_FORCEUID, val);
508     } else if (optionok(tmp, "forcegid:", val)) {
509         setoption(options, save, VOLOPT_FORCEGID, val);
510
511 #endif /* FORCE_UIDGID */
512     } else if (optionok(tmp, "root_preexec:", val)) {
513         setoption(options, save, VOLOPT_ROOTPREEXEC, val);
514
515     } else if (optionok(tmp, "preexec:", val)) {
516         setoption(options, save, VOLOPT_PREEXEC, val);
517
518     } else if (optionok(tmp, "root_postexec:", val)) {
519         setoption(options, save, VOLOPT_ROOTPOSTEXEC, val);
520
521     } else if (optionok(tmp, "postexec:", val)) {
522         setoption(options, save, VOLOPT_POSTEXEC, val);
523
524     } else if (optionok(tmp, "allowed_hosts:", val)) {
525         setoption(options, save, VOLOPT_ALLOWED_HOSTS, val);
526
527     } else if (optionok(tmp, "denied_hosts:", val)) {
528         setoption(options, save, VOLOPT_DENIED_HOSTS, val);
529
530     } else if (optionok(tmp, "ea:", val)) {
531         if (strcasecmp(val + 1, "ad") == 0)
532             options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AD;
533         else if (strcasecmp(val + 1, "sys") == 0)
534             options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_SYS;
535         else if (strcasecmp(val + 1, "none") == 0)
536             options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_NONE;
537
538     } else {
539         /* ignore unknown options */
540         LOG(log_debug, logtype_afpd, "ignoring unknown volume option: %s", tmp);
541
542     }
543 }
544
545 /* ----------------- */
546 static void showvol(const ucs2_t *name)
547 {
548     struct vol  *volume;
549     for ( volume = Volumes; volume; volume = volume->v_next ) {
550         if (volume->v_hide && !strcasecmp_w( volume->v_name, name ) ) {
551             volume->v_hide = 0;
552             return;
553         }
554     }
555 }
556
557 /* ------------------------------- */
558 static int creatvol(AFPObj *obj, struct passwd *pwd,
559                     char *path, char *name,
560                     struct vol_option *options,
561                     const int user /* user defined volume */
562     )
563 {
564     struct vol  *volume;
565     int         suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
566     int         hide = 0;
567     char        tmpname[AFPVOL_U8MNAMELEN+1];
568     ucs2_t      u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
569     char        suffix[6]; /* max is #FFFF */
570     u_int16_t   flags;
571
572     if ( name == NULL || *name == '\0' ) {
573         if ((name = strrchr( path, '/' )) == NULL) {
574             return -1;  /* Obviously not a fully qualified path */
575         }
576
577         /* if you wish to share /, you need to specify a name. */
578         if (*++name == '\0')
579             return -1;
580     }
581
582     /* suffix for mangling use (lastvid + 1)   */
583     /* because v_vid has not been decided yet. */
584     suffixlen = sprintf(suffix, "%c%X", MANGLE_CHAR, lastvid + 1 );
585
586     vlen = strlen( name );
587
588     /* Unicode Volume Name */
589     /* Firstly convert name from unixcharset to UTF8-MAC */
590     flags = CONV_IGNORE;
591     tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
592     if (tmpvlen <= 0) {
593         strcpy(tmpname, "???");
594         tmpvlen = 3;
595     }
596
597     /* Do we have to mangle ? */
598     if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
599         if (tmpvlen + suffixlen > obj->options.volnamelen) {
600             flags = CONV_FORCE;
601             tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
602             tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
603         }
604         strcat(tmpname, suffix);
605         tmpvlen = strlen(tmpname);
606     }
607
608     /* Secondly convert name from UTF8-MAC to UCS2 */
609     if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
610         return -1;
611
612     LOG(log_debug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
613
614     /* Maccharset Volume Name */
615     /* Firsty convert name from unixcharset to maccharset */
616     flags = CONV_IGNORE;
617     tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
618     if (tmpvlen <= 0) {
619         strcpy(tmpname, "???");
620         tmpvlen = 3;
621     }
622
623     /* Do we have to mangle ? */
624     if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
625         if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
626             flags = CONV_FORCE;
627             tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_MACNAMELEN - suffixlen, &flags);
628             tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
629         }
630         strcat(tmpname, suffix);
631         tmpvlen = strlen(tmpname);
632     }
633
634     /* Secondly convert name from maccharset to UCS2 */
635     if ( 0 >= ( macvlen = convert_string(obj->options.maccharset, CH_UCS2, tmpname, tmpvlen, mactmpname, AFPVOL_U8MNAMELEN*2)) )
636         return -1;
637
638     LOG(log_debug, logtype_afpd, "createvol: Volume '%s' ->  Longname: '%s'", name, tmpname);
639
640     /* check duplicate */
641     for ( volume = Volumes; volume; volume = volume->v_next ) {
642         if (( strcasecmp_w( volume->v_u8mname, u8mtmpname ) == 0 ) || ( strcasecmp_w( volume->v_macname, mactmpname ) == 0 )){
643             if (volume->v_deleted) {
644                 volume->v_new = hide = 1;
645             }
646             else {
647                 return -1;  /* Won't be able to access it, anyway... */
648             }
649         }
650     }
651
652     if (!( volume = (struct vol *)calloc(1, sizeof( struct vol ))) ) {
653         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
654         return -1;
655     }
656     if ( NULL == ( volume->v_localname = strdup(name))) {
657         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
658         free(volume);
659         return -1;
660     }
661
662     if ( NULL == ( volume->v_u8mname = strdup_w(u8mtmpname))) {
663         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
664         volume_free(volume);
665         free(volume);
666         return -1;
667     }
668     if ( NULL == ( volume->v_macname = strdup_w(mactmpname))) {
669         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
670         volume_free(volume);
671         free(volume);
672         return -1;
673     }
674     if (!( volume->v_path = (char *)malloc( strlen( path ) + 1 )) ) {
675         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
676         volume_free(volume);
677         free(volume);
678         return -1;
679     }
680
681     volume->v_name = utf8_encoding()?volume->v_u8mname:volume->v_macname;
682     volume->v_hide = hide;
683     strcpy( volume->v_path, path );
684
685 #ifdef __svr4__
686     volume->v_qfd = -1;
687 #endif /* __svr4__ */
688     /* os X start at 1 and use network order ie. 1 2 3 */
689     volume->v_vid = ++lastvid;
690     volume->v_vid = htons(volume->v_vid);
691
692     /* handle options */
693     if (options) {
694         /* should we casefold? */
695         volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
696
697         /* shift in some flags */
698         volume->v_flags = options[VOLOPT_FLAGS].i_value;
699
700         if (options[VOLOPT_EA_VFS].i_value)
701             volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
702
703         volume->v_ad_options = 0;
704         if ((volume->v_flags & AFPVOL_NODEV))
705             volume->v_ad_options |= ADVOL_NODEV;
706         if ((volume->v_flags & AFPVOL_CACHE))
707             volume->v_ad_options |= ADVOL_CACHE;
708         if ((volume->v_flags & AFPVOL_UNIX_PRIV))
709             volume->v_ad_options |= ADVOL_UNIXPRIV;
710         if ((volume->v_flags & AFPVOL_INV_DOTS))
711             volume->v_ad_options |= ADVOL_INVDOTS;
712         if ((volume->v_flags & AFPVOL_NOADOUBLE))
713             volume->v_ad_options |= ADVOL_NOADOUBLE;
714
715         if (options[VOLOPT_PASSWORD].c_value)
716             volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
717
718         if (options[VOLOPT_VETO].c_value)
719             volume->v_veto = strdup(options[VOLOPT_VETO].c_value);
720
721         if (options[VOLOPT_ENCODING].c_value)
722             volume->v_volcodepage = strdup(options[VOLOPT_ENCODING].c_value);
723
724         if (options[VOLOPT_MACCHARSET].c_value)
725             volume->v_maccodepage = strdup(options[VOLOPT_MACCHARSET].c_value);
726
727         if (options[VOLOPT_DBPATH].c_value)
728             volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_DBPATH].c_value, pwd, path, name);
729
730         if (options[VOLOPT_CNIDSCHEME].c_value)
731             volume->v_cnidscheme = strdup(options[VOLOPT_CNIDSCHEME].c_value);
732
733         if (options[VOLOPT_CNIDSERVER].c_value)
734             volume->v_cnidserver = strdup(options[VOLOPT_CNIDSERVER].c_value);
735
736         if (options[VOLOPT_CNIDPORT].c_value)
737             volume->v_cnidport = strdup(options[VOLOPT_CNIDPORT].c_value);
738
739         if (options[VOLOPT_UMASK].i_value)
740             volume->v_umask = (mode_t)options[VOLOPT_UMASK].i_value;
741
742         if (options[VOLOPT_DPERM].i_value)
743             volume->v_dperm = (mode_t)options[VOLOPT_DPERM].i_value;
744
745         if (options[VOLOPT_FPERM].i_value)
746             volume->v_fperm = (mode_t)options[VOLOPT_FPERM].i_value;
747
748         if (options[VOLOPT_DFLTPERM].i_value)
749             volume->v_perm = (mode_t)options[VOLOPT_DFLTPERM].i_value;
750
751         if (options[VOLOPT_ADOUBLE].i_value)
752             volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
753         else
754             volume->v_adouble = AD_VERSION;
755
756         /* Mac to Unix conversion flags*/
757         volume->v_mtou_flags = 0;
758         if (!(volume->v_flags & AFPVOL_NOHEX))
759             volume->v_mtou_flags |= CONV_ESCAPEHEX;
760         if (!(volume->v_flags & AFPVOL_USEDOTS))
761             volume->v_mtou_flags |= CONV_ESCAPEDOTS;
762         if ((volume->v_flags & AFPVOL_EILSEQ))
763             volume->v_mtou_flags |= CONV__EILSEQ;
764
765         if ((volume->v_casefold & AFPVOL_MTOUUPPER))
766             volume->v_mtou_flags |= CONV_TOUPPER;
767         else if ((volume->v_casefold & AFPVOL_MTOULOWER))
768             volume->v_mtou_flags |= CONV_TOLOWER;
769
770         /* Unix to Mac conversion flags*/
771         volume->v_utom_flags = CONV_IGNORE | CONV_UNESCAPEHEX;
772         if ((volume->v_casefold & AFPVOL_UTOMUPPER))
773             volume->v_utom_flags |= CONV_TOUPPER;
774         else if ((volume->v_casefold & AFPVOL_UTOMLOWER))
775             volume->v_utom_flags |= CONV_TOLOWER;
776
777         if ((volume->v_flags & AFPVOL_EILSEQ))
778             volume->v_utom_flags |= CONV__EILSEQ;
779
780 #ifdef FORCE_UIDGID
781         if (options[VOLOPT_FORCEUID].c_value) {
782             volume->v_forceuid = strdup(options[VOLOPT_FORCEUID].c_value);
783         } else {
784             volume->v_forceuid = NULL; /* set as null so as to return 0 later on */
785         }
786
787         if (options[VOLOPT_FORCEGID].c_value) {
788             volume->v_forcegid = strdup(options[VOLOPT_FORCEGID].c_value);
789         } else {
790             volume->v_forcegid = NULL; /* set as null so as to return 0 later on */
791         }
792 #endif
793         if (!user) {
794             if (options[VOLOPT_PREEXEC].c_value)
795                 volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_PREEXEC].c_value, pwd, path, name);
796             volume->v_preexec_close = options[VOLOPT_PREEXEC].i_value;
797
798             if (options[VOLOPT_POSTEXEC].c_value)
799                 volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_POSTEXEC].c_value, pwd, path, name);
800
801             if (options[VOLOPT_ROOTPREEXEC].c_value)
802                 volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPREEXEC].c_value, pwd, path,  name);
803             volume->v_root_preexec_close = options[VOLOPT_ROOTPREEXEC].i_value;
804
805             if (options[VOLOPT_ROOTPOSTEXEC].c_value)
806                 volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPOSTEXEC].c_value, pwd, path,  name);
807         }
808     }
809     volume->v_dperm |= volume->v_perm;
810     volume->v_fperm |= volume->v_perm;
811
812     /* Check EA support on volume */
813     if (volume->v_vfs_ea == AFPVOL_EA_AUTO)
814         check_ea_sys_support(volume);
815     initvol_vfs(volume);
816
817     volume->v_next = Volumes;
818     Volumes = volume;
819     return 0;
820 }
821
822 /* ---------------- */
823 static char *myfgets( char *buf, int size, FILE *fp)
824 {
825     char    *p;
826     int     c;
827
828     p = buf;
829     while ((EOF != ( c = getc( fp )) ) && ( size > 1 )) {
830         if ( c == '\n' || c == '\r' ) {
831             if (p != buf && *(p -1) == '\\') {
832                 p--;
833                 size++;
834                 continue;
835             }
836             *p++ = '\n';
837             break;
838         } else {
839             *p++ = c;
840         }
841         size--;
842     }
843
844     if ( p == buf ) {
845         return( NULL );
846     } else {
847         *p = '\0';
848         return( buf );
849     }
850 }
851
852
853 /* check access list. this function wants something of the following
854  * form:
855  *        @group,name,name2,@group2,name3
856  *
857  * a NULL argument allows everybody to have access.
858  * we return three things:
859  *     -1: no list
860  *      0: list exists, but name isn't in it
861  *      1: in list
862  */
863
864 #ifndef NO_REAL_USER_NAME
865 /* authentication is case insensitive
866  * FIXME should we do the same with group name?
867  */
868 #define access_strcmp strcasecmp
869
870 #else
871 #define access_strcmp strcmp
872
873 #endif
874
875 static int accessvol(const char *args, const char *name)
876 {
877     char buf[MAXPATHLEN + 1], *p;
878     struct group *gr;
879
880     if (!args)
881         return -1;
882
883     strlcpy(buf, args, sizeof(buf));
884     if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
885         return -1;
886
887     while (p) {
888         if (*p == '@') { /* it's a group */
889             if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid))
890                 return 1;
891         } else if (access_strcmp(p, name) == 0) /* it's a user name */
892             return 1;
893         p = strtok(NULL, ",");
894     }
895
896     return 0;
897 }
898
899 static int hostaccessvol(int type, const char *volname, const char *args, const AFPObj *obj)
900 {
901     int mask_int;
902     char buf[MAXPATHLEN + 1], *p, *b;
903     DSI *dsi = obj->handle;
904     struct sockaddr_storage client;
905
906     if (!args)
907         return -1;
908
909     strlcpy(buf, args, sizeof(buf));
910     if ((p = strtok_r(buf, ",", &b)) == NULL) /* nothing, return okay */
911         return -1;
912
913     if (obj->proto != AFPPROTO_DSI)
914         return -1;
915
916     while (p) {
917         int ret;
918         char *ipaddr, *mask_char;
919         struct addrinfo hints, *ai;
920
921         ipaddr = strtok(p, "/");
922         mask_char = strtok(NULL,"/");
923
924         /* Get address from string with getaddrinfo */
925         memset(&hints, 0, sizeof hints);
926         hints.ai_family = AF_UNSPEC;
927         hints.ai_socktype = SOCK_STREAM;
928         if ((ret = getaddrinfo(ipaddr, NULL, &hints, &ai)) != 0) {
929             LOG(log_error, logtype_afpd, "hostaccessvol: getaddrinfo: %s\n", gai_strerror(ret));
930             continue;
931         }
932
933         /* netmask */
934         if (mask_char != NULL)
935             mask_int = atoi(mask_char); /* apply_ip_mask does range checking on it */
936         else {
937             if (ai->ai_family == AF_INET) /* IPv4 */
938                 mask_int = 32;
939             else                          /* IPv6 */
940                 mask_int = 128;
941         }
942
943         /* Apply mask to addresses */
944         client = dsi->client;
945         apply_ip_mask((struct sockaddr *)&client, mask_int);
946         apply_ip_mask(ai->ai_addr, mask_int);
947
948         if (compare_ip((struct sockaddr *)&client, ai->ai_addr) == 0) {
949             if (type == VOLOPT_DENIED_HOSTS)
950                 LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s' by denied list",
951                     getip_string((struct sockaddr *)&client), volname);
952             freeaddrinfo(ai);
953             return 1;
954         }
955
956         /* next address */
957         freeaddrinfo(ai);
958         p = strtok_r(NULL, ",", &b);
959     }
960
961     if (type == VOLOPT_ALLOWED_HOSTS)
962         LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s', not in allowed list",
963             getip_string((struct sockaddr *)&dsi->client), volname);
964     return 0;
965 }
966
967 static void setextmap(char *ext, char *type, char *creator, int user)
968 {
969     struct extmap   *em;
970     int                 cnt;
971
972     if (Extmap == NULL) {
973         if (( Extmap = calloc(1, sizeof( struct extmap ))) == NULL ) {
974             LOG(log_error, logtype_afpd, "setextmap: calloc: %s", strerror(errno) );
975             return;
976         }
977     }
978     ext++;
979     for ( em = Extmap, cnt = 0; em->em_ext; em++, cnt++) {
980         if ( (strdiacasecmp( em->em_ext, ext )) == 0 ) {
981             break;
982         }
983     }
984
985     if ( em->em_ext == NULL ) {
986         if (!(Extmap  = realloc( Extmap, sizeof( struct extmap ) * (cnt +2))) ) {
987             LOG(log_error, logtype_afpd, "setextmap: realloc: %s", strerror(errno) );
988             return;
989         }
990         (Extmap +cnt +1)->em_ext = NULL;
991         em = Extmap +cnt;
992     } else if ( !user ) {
993         return;
994     }
995     if (em->em_ext)
996         free(em->em_ext);
997
998     if (!(em->em_ext = strdup(  ext))) {
999         LOG(log_error, logtype_afpd, "setextmap: strdup: %s", strerror(errno) );
1000         return;
1001     }
1002
1003     if ( *type == '\0' ) {
1004         memcpy(em->em_type, "\0\0\0\0", sizeof( em->em_type ));
1005     } else {
1006         memcpy(em->em_type, type, sizeof( em->em_type ));
1007     }
1008     if ( *creator == '\0' ) {
1009         memcpy(em->em_creator, "\0\0\0\0", sizeof( em->em_creator ));
1010     } else {
1011         memcpy(em->em_creator, creator, sizeof( em->em_creator ));
1012     }
1013 }
1014
1015 /* -------------------------- */
1016 static int extmap_cmp(const void *map1, const void *map2)
1017 {
1018     const struct extmap *em1 = map1;
1019     const struct extmap *em2 = map2;
1020     return strdiacasecmp(em1->em_ext, em2->em_ext);
1021 }
1022
1023 static void sortextmap( void)
1024 {
1025     struct extmap   *em;
1026
1027     Extmap_cnt = 0;
1028     if ((em = Extmap) == NULL) {
1029         return;
1030     }
1031     while (em->em_ext) {
1032         em++;
1033         Extmap_cnt++;
1034     }
1035     if (Extmap_cnt) {
1036         qsort(Extmap, Extmap_cnt, sizeof(struct extmap), extmap_cmp);
1037         if (*Extmap->em_ext == 0) {
1038             /* the first line is really "." the default entry,
1039              * we remove the leading '.' in setextmap
1040              */
1041             Defextmap = Extmap;
1042         }
1043     }
1044 }
1045
1046 /* ----------------------
1047  */
1048 static void free_extmap( void)
1049 {
1050     struct extmap   *em;
1051
1052     if (Extmap) {
1053         for ( em = Extmap; em->em_ext; em++) {
1054             free (em->em_ext);
1055         }
1056         free(Extmap);
1057         Extmap = NULL;
1058         Defextmap = Extmap;
1059         Extmap_cnt = 0;
1060     }
1061 }
1062
1063 /* ----------------------
1064  */
1065 static int volfile_changed(struct afp_volume_name *p)
1066 {
1067     struct stat      st;
1068     char *name;
1069
1070     if (p->full_name)
1071         name = p->full_name;
1072     else
1073         name = p->name;
1074
1075     if (!stat( name, &st) && st.st_mtime > p->mtime) {
1076         p->mtime = st.st_mtime;
1077         return 1;
1078     }
1079     return 0;
1080 }
1081
1082 /* ----------------------
1083  * Read a volume configuration file and add the volumes contained within to
1084  * the global volume list.  If p2 is non-NULL, the file that is opened is
1085  * p1/p2
1086  *
1087  * Lines that begin with # and blank lines are ignored.
1088  * Volume lines are of the form:
1089  *      <unix path> [<volume name>] [allow:<user>,<@group>,...] \
1090  *                           [codepage:<file>] [casefold:<num>]
1091  *      <extension> TYPE [CREATOR]
1092  */
1093 static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
1094 {
1095     FILE        *fp;
1096     char        path[MAXPATHLEN + 1];
1097     char        tmp[MAXPATHLEN + 1];
1098     char        volname[AFPVOL_U8MNAMELEN + 1];
1099     char        buf[BUFSIZ];
1100     char        type[5], creator[5];
1101     char        *u, *p;
1102     struct passwd   *pw;
1103     struct vol_option   save_options[VOLOPT_NUM];
1104     struct vol_option   options[VOLOPT_NUM];
1105     int                 i;
1106     struct stat         st;
1107     int                 fd;
1108
1109     if (!p1->name)
1110         return -1;
1111     p1->mtime = 0;
1112     strcpy( path, p1->name );
1113     if ( p2 != NULL ) {
1114         strcat( path, "/" );
1115         strcat( path, p2 );
1116         if (p1->full_name) {
1117             free(p1->full_name);
1118         }
1119         p1->full_name = strdup(path);
1120     }
1121
1122     if (NULL == ( fp = fopen( path, "r" )) ) {
1123         return( -1 );
1124     }
1125     fd = fileno(fp);
1126     if (fd != -1 && !fstat( fd, &st) ) {
1127         p1->mtime = st.st_mtime;
1128     }
1129
1130     memset(save_options, 0, sizeof(save_options));
1131
1132     /* Enable some default options for all volumes */
1133     save_options[VOLOPT_FLAGS].i_value |= AFPVOL_CACHE;
1134     save_options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AUTO;
1135
1136     while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) {
1137         initline( strlen( buf ), buf );
1138         parseline( sizeof( path ) - 1, path );
1139         switch ( *path ) {
1140         case '\0' :
1141         case '#' :
1142             continue;
1143
1144         case ':':
1145             /* change the default options for this file */
1146             if (strncmp(path, VOLOPT_DEFAULT, VOLOPT_DEFAULT_LEN) == 0) {
1147                 *tmp = '\0';
1148                 for (i = 0; i < VOLOPT_NUM; i++) {
1149                     if (parseline( sizeof( path ) - VOLOPT_DEFAULT_LEN - 1,
1150                                    path + VOLOPT_DEFAULT_LEN) < 0)
1151                         break;
1152                     volset(save_options, NULL, tmp, sizeof(tmp) - 1,
1153                            path + VOLOPT_DEFAULT_LEN);
1154                 }
1155             }
1156             break;
1157
1158         case '~' :
1159             if (( p = strchr( path, '/' )) != NULL ) {
1160                 *p++ = '\0';
1161             }
1162             u = path;
1163             u++;
1164             if ( *u == '\0' ) {
1165                 u = obj->username;
1166             }
1167             if ( u == NULL || *u == '\0' || ( pw = getpwnam( u )) == NULL ) {
1168                 continue;
1169             }
1170             strcpy( tmp, pw->pw_dir );
1171             if ( p != NULL && *p != '\0' ) {
1172                 strcat( tmp, "/" );
1173                 strcat( tmp, p );
1174             }
1175             /* Tag a user's home directory with their umask.  Note, this will
1176              * be overwritten if the user actually specifies a umask: option
1177              * for a '~' volume. */
1178             save_options[VOLOPT_UMASK].i_value = obj->options.save_mask;
1179             /* fall through */
1180
1181         case '/' :
1182             /* send path through variable substitution */
1183             if (*path != '~') /* need to copy path to tmp */
1184                 strcpy(tmp, path);
1185             if (!pwent)
1186                 pwent = getpwnam(obj->username);
1187             volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL);
1188
1189             /* this is sort of braindead. basically, i want to be
1190              * able to specify things in any order, but i don't want to
1191              * re-write everything.
1192              *
1193              * currently we have options:
1194              *   volname
1195              *   codepage:x
1196              *   casefold:x
1197              *   allow:x,y,@z
1198              *   deny:x,y,@z
1199              *   rwlist:x,y,@z
1200              *   rolist:x,y,@z
1201              *   options:prodos,crlf,noadouble,ro...
1202              *   dbpath:x
1203              *   password:x
1204              *   preexec:x
1205              *
1206              *   namemask:x,y,!z  (not implemented yet)
1207              */
1208             memcpy(options, save_options, sizeof(options));
1209             *volname = '\0';
1210
1211             /* read in up to VOLOP_NUM possible options */
1212             for (i = 0; i < VOLOPT_NUM; i++) {
1213                 if (parseline( sizeof( tmp ) - 1, tmp ) < 0)
1214                     break;
1215
1216                 volset(options, save_options, volname, sizeof(volname) - 1, tmp);
1217             }
1218
1219             /* check allow/deny lists:
1220                allow -> either no list (-1), or in list (1)
1221                deny -> either no list (-1), or not in list (0) */
1222             if (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
1223                 (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) &&
1224                 hostaccessvol(VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value, obj) &&
1225                 (hostaccessvol(VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value, obj) < 1)) {
1226
1227                 /* handle read-only behaviour. semantics:
1228                  * 1) neither the rolist nor the rwlist exist -> rw
1229                  * 2) rolist exists -> ro if user is in it.
1230                  * 3) rwlist exists -> ro unless user is in it. */
1231                 if (((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0) &&
1232                     ((accessvol(options[VOLOPT_ROLIST].c_value,
1233                                 obj->username) == 1) ||
1234                      !accessvol(options[VOLOPT_RWLIST].c_value,
1235                                 obj->username)))
1236                     options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
1237
1238                 /* do variable substitution for volname */
1239                 volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL);
1240                 creatvol(obj, pwent, path, tmp, options, p2 != NULL);
1241             }
1242             volfree(options, save_options);
1243             break;
1244
1245         case '.' :
1246             parseline( sizeof( type ) - 1, type );
1247             parseline( sizeof( creator ) - 1, creator );
1248             setextmap( path, type, creator, user);
1249             break;
1250
1251         default :
1252             break;
1253         }
1254     }
1255     volfree(save_options, NULL);
1256     sortextmap();
1257     if ( fclose( fp ) != 0 ) {
1258         LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
1259     }
1260     p1->loaded = 1;
1261     return( 0 );
1262 }
1263
1264 /* ------------------------------- */
1265 static void volume_free(struct vol *vol)
1266 {
1267     free(vol->v_localname);
1268     vol->v_localname = NULL;
1269     free(vol->v_u8mname);
1270     vol->v_u8mname = NULL;
1271     free(vol->v_macname);
1272     vol->v_macname = NULL;
1273     free(vol->v_path);
1274     free(vol->v_password);
1275     free(vol->v_veto);
1276     free(vol->v_volcodepage);
1277     free(vol->v_maccodepage);
1278     free(vol->v_cnidscheme);
1279     free(vol->v_dbpath);
1280     free(vol->v_gvs);
1281 #ifdef FORCE_UIDGID
1282     free(vol->v_forceuid);
1283     free(vol->v_forcegid);
1284 #endif /* FORCE_UIDGID */
1285 }
1286
1287 /* ------------------------------- */
1288 static void free_volumes(void )
1289 {
1290     struct vol  *vol;
1291     struct vol  *nvol, *ovol;
1292
1293     for ( vol = Volumes; vol; vol = vol->v_next ) {
1294         if (( vol->v_flags & AFPVOL_OPEN ) ) {
1295             vol->v_deleted = 1;
1296             continue;
1297         }
1298         volume_free(vol);
1299     }
1300
1301     for ( vol = Volumes, ovol = NULL; vol; vol = nvol) {
1302         nvol = vol->v_next;
1303
1304         if (vol->v_localname == NULL) {
1305             if (Volumes == vol) {
1306                 Volumes = nvol;
1307                 ovol = Volumes;
1308             }
1309             else {
1310                 ovol->v_next = nvol;
1311             }
1312             free(vol);
1313         }
1314         else {
1315             ovol = vol;
1316         }
1317     }
1318 }
1319
1320 /* ------------------------------- */
1321 static void volume_unlink(struct vol *volume)
1322 {
1323     struct vol *vol, *ovol, *nvol;
1324
1325     if (volume == Volumes) {
1326         Volumes = Volumes->v_next;
1327         return;
1328     }
1329     for ( vol = Volumes->v_next, ovol = Volumes; vol; vol = nvol) {
1330         nvol = vol->v_next;
1331
1332         if (vol == volume) {
1333             ovol->v_next = nvol;
1334             break;
1335         }
1336         else {
1337             ovol = vol;
1338         }
1339     }
1340 }
1341
1342 static int getvolspace(struct vol *vol,
1343                        u_int32_t *bfree, u_int32_t *btotal,
1344                        VolSpace *xbfree, VolSpace *xbtotal, u_int32_t *bsize)
1345 {
1346     int         spaceflag, rc;
1347     u_int32_t   maxsize;
1348 #ifndef NO_QUOTA_SUPPORT
1349     VolSpace    qfree, qtotal;
1350 #endif
1351
1352     spaceflag = AFPVOL_GVSMASK & vol->v_flags;
1353     /* report up to 2GB if afp version is < 2.2 (4GB if not) */
1354     maxsize = (vol->v_flags & AFPVOL_A2VOL) ? 0x01fffe00 :
1355         (((afp_version < 22) || (vol->v_flags & AFPVOL_LIMITSIZE))
1356          ? 0x7fffffffL : 0xffffffffL);
1357
1358 #ifdef AFS
1359     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) {
1360         if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) {
1361             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS;
1362             goto getvolspace_done;
1363         }
1364     }
1365 #endif
1366
1367     if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal,
1368                                     bsize)) != AFP_OK ) {
1369         return( rc );
1370     }
1371
1372 #define min(a,b)    ((a)<(b)?(a):(b))
1373 #ifndef NO_QUOTA_SUPPORT
1374     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) {
1375         if ( uquota_getvolspace( vol, &qfree, &qtotal, *bsize ) == AFP_OK ) {
1376             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA;
1377             *xbfree = min(*xbfree, qfree);
1378             *xbtotal = min( *xbtotal, qtotal);
1379             goto getvolspace_done;
1380         }
1381     }
1382 #endif
1383     vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
1384
1385 getvolspace_done:
1386     *bfree = min( *xbfree, maxsize);
1387     *btotal = min( *xbtotal, maxsize);
1388     return( AFP_OK );
1389 }
1390
1391 /* -----------------------
1392  * set volume creation date
1393  * avoid duplicate, well at least it tries
1394  */
1395 static void vol_setdate(u_int16_t id, struct adouble *adp, time_t date)
1396 {
1397     struct vol  *volume;
1398     struct vol  *vol = Volumes;
1399
1400     for ( volume = Volumes; volume; volume = volume->v_next ) {
1401         if (volume->v_vid == id) {
1402             vol = volume;
1403         }
1404         else if ((time_t)(AD_DATE_FROM_UNIX(date)) == volume->v_ctime) {
1405             date = date+1;
1406             volume = Volumes; /* restart */
1407         }
1408     }
1409     vol->v_ctime = AD_DATE_FROM_UNIX(date);
1410     ad_setdate(adp, AD_DATE_CREATE | AD_DATE_UNIX, date);
1411 }
1412
1413 /* ----------------------- */
1414 static int getvolparams( u_int16_t bitmap, struct vol *vol, struct stat *st, char *buf, size_t *buflen)
1415 {
1416     struct adouble  ad;
1417     int         bit = 0, isad = 1;
1418     u_int32_t       aint;
1419     u_short     ashort;
1420     u_int32_t       bfree, btotal, bsize;
1421     VolSpace            xbfree, xbtotal; /* extended bytes */
1422     char        *data, *nameoff = NULL;
1423     char                *slash;
1424
1425     LOG(log_debug, logtype_afpd, "getvolparams: Volume '%s'", vol->v_localname);
1426
1427     /* courtesy of jallison@whistle.com:
1428      * For MacOS8.x support we need to create the
1429      * .Parent file here if it doesn't exist. */
1430
1431     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1432     if ( ad_open_metadata( vol->v_path, ADFLAGS_DIR, O_CREAT, &ad) < 0 ) {
1433         isad = 0;
1434         vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
1435
1436     } else if (ad_get_MD_flags( &ad ) & O_CREAT) {
1437         slash = strrchr( vol->v_path, '/' );
1438         if(slash)
1439             slash++;
1440         else
1441             slash = vol->v_path;
1442         if (ad_getentryoff(&ad, ADEID_NAME)) {
1443             ad_setentrylen( &ad, ADEID_NAME, strlen( slash ));
1444             memcpy(ad_entry( &ad, ADEID_NAME ), slash,
1445                    ad_getentrylen( &ad, ADEID_NAME ));
1446         }
1447         vol_setdate(vol->v_vid, &ad, st->st_mtime);
1448         ad_flush(&ad);
1449     }
1450     else {
1451         if (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0)
1452             vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
1453         else
1454             vol->v_ctime = aint;
1455     }
1456
1457     if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) |
1458                      (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) |
1459                      (1<<VOLPBIT_BSIZE)) ) != 0 ) {
1460         if ( getvolspace( vol, &bfree, &btotal, &xbfree, &xbtotal,
1461                           &bsize) != AFP_OK ) {
1462             if ( isad ) {
1463                 ad_close( &ad, ADFLAGS_HF );
1464             }
1465             return( AFPERR_PARAM );
1466         }
1467     }
1468
1469     data = buf;
1470     while ( bitmap != 0 ) {
1471         while (( bitmap & 1 ) == 0 ) {
1472             bitmap = bitmap>>1;
1473             bit++;
1474         }
1475
1476         switch ( bit ) {
1477         case VOLPBIT_ATTR :
1478             ashort = 0;
1479             /* check for read-only.
1480              * NOTE: we don't actually set the read-only flag unless
1481              *       it's passed in that way as it's possible to mount
1482              *       a read-write filesystem under a read-only one. */
1483             if ((vol->v_flags & AFPVOL_RO) ||
1484                 ((utime(vol->v_path, NULL) < 0) && (errno == EROFS))) {
1485                 ashort |= VOLPBIT_ATTR_RO;
1486             }
1487             /* prior 2.1 only VOLPBIT_ATTR_RO is defined */
1488             if (afp_version > 20) {
1489                 if (0 == (vol->v_flags & AFPVOL_NOFILEID) && vol->v_cdb != NULL &&
1490                     (vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1491                     ashort |= VOLPBIT_ATTR_FILEID;
1492                 }
1493                 ashort |= VOLPBIT_ATTR_CATSEARCH;
1494
1495                 if (afp_version >= 30) {
1496                     ashort |= VOLPBIT_ATTR_UTF8;
1497                     if (vol->v_flags & AFPVOL_UNIX_PRIV)
1498                         ashort |= VOLPBIT_ATTR_UNIXPRIV;
1499                     if (vol->v_flags & AFPVOL_TM)
1500                         ashort |= VOLPBIT_ATTR_TM;
1501
1502                     if (afp_version >= 32) {
1503                         if (vol->v_vfs_ea)
1504                             ashort |= VOLPBIT_ATTR_EXT_ATTRS;
1505                         if (vol->v_flags & AFPVOL_ACLS)
1506                             ashort |= VOLPBIT_ATTR_ACLS;
1507                     }
1508                 }
1509             }
1510             ashort = htons(ashort);
1511             memcpy(data, &ashort, sizeof( ashort ));
1512             data += sizeof( ashort );
1513             break;
1514
1515         case VOLPBIT_SIG :
1516             ashort = htons( AFPVOLSIG_DEFAULT );
1517             memcpy(data, &ashort, sizeof( ashort ));
1518             data += sizeof( ashort );
1519             break;
1520
1521         case VOLPBIT_CDATE :
1522             aint = vol->v_ctime;
1523             memcpy(data, &aint, sizeof( aint ));
1524             data += sizeof( aint );
1525             break;
1526
1527         case VOLPBIT_MDATE :
1528             if ( st->st_mtime > vol->v_mtime ) {
1529                 vol->v_mtime = st->st_mtime;
1530             }
1531             aint = AD_DATE_FROM_UNIX(vol->v_mtime);
1532             memcpy(data, &aint, sizeof( aint ));
1533             data += sizeof( aint );
1534             break;
1535
1536         case VOLPBIT_BDATE :
1537             if (!isad ||  (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1538                 aint = AD_DATE_START;
1539             memcpy(data, &aint, sizeof( aint ));
1540             data += sizeof( aint );
1541             break;
1542
1543         case VOLPBIT_VID :
1544             memcpy(data, &vol->v_vid, sizeof( vol->v_vid ));
1545             data += sizeof( vol->v_vid );
1546             break;
1547
1548         case VOLPBIT_BFREE :
1549             bfree = htonl( bfree );
1550             memcpy(data, &bfree, sizeof( bfree ));
1551             data += sizeof( bfree );
1552             break;
1553
1554         case VOLPBIT_BTOTAL :
1555             btotal = htonl( btotal );
1556             memcpy(data, &btotal, sizeof( btotal ));
1557             data += sizeof( btotal );
1558             break;
1559
1560 #ifndef NO_LARGE_VOL_SUPPORT
1561         case VOLPBIT_XBFREE :
1562             xbfree = hton64( xbfree );
1563 #if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
1564             bcopy(&xbfree, data, sizeof(xbfree));
1565 #else /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1566             memcpy(data, &xbfree, sizeof( xbfree ));
1567 #endif /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1568             data += sizeof( xbfree );
1569             break;
1570
1571         case VOLPBIT_XBTOTAL :
1572             xbtotal = hton64( xbtotal );
1573 #if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
1574             bcopy(&xbtotal, data, sizeof(xbtotal));
1575 #else /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1576             memcpy(data, &xbtotal, sizeof( xbtotal ));
1577 #endif /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1578             data += sizeof( xbfree );
1579             break;
1580 #endif /* ! NO_LARGE_VOL_SUPPORT */
1581
1582         case VOLPBIT_NAME :
1583             nameoff = data;
1584             data += sizeof( u_int16_t );
1585             break;
1586
1587         case VOLPBIT_BSIZE:  /* block size */
1588             bsize = htonl(bsize);
1589             memcpy(data, &bsize, sizeof(bsize));
1590             data += sizeof(bsize);
1591             break;
1592
1593         default :
1594             if ( isad ) {
1595                 ad_close( &ad, ADFLAGS_HF );
1596             }
1597             return( AFPERR_BITMAP );
1598         }
1599         bitmap = bitmap>>1;
1600         bit++;
1601     }
1602     if ( nameoff ) {
1603         ashort = htons( data - buf );
1604         memcpy(nameoff, &ashort, sizeof( ashort ));
1605         /* name is always in mac charset */
1606         aint = ucs2_to_charset( vol->v_maccharset, vol->v_macname, data+1, AFPVOL_MACNAMELEN + 1);
1607         if ( aint <= 0 ) {
1608             *buflen = 0;
1609             return AFPERR_MISC;
1610         }
1611
1612         *data++ = aint;
1613         data += aint;
1614     }
1615     if ( isad ) {
1616         ad_close_metadata( &ad);
1617     }
1618     *buflen = data - buf;
1619     return( AFP_OK );
1620 }
1621
1622 /* ------------------------- */
1623 static int stat_vol(u_int16_t bitmap, struct vol *vol, char *rbuf, size_t *rbuflen)
1624 {
1625     struct stat st;
1626     int     ret;
1627     size_t  buflen;
1628
1629     if ( stat( vol->v_path, &st ) < 0 ) {
1630         *rbuflen = 0;
1631         return( AFPERR_PARAM );
1632     }
1633     /* save the volume device number */
1634     vol->v_dev = st.st_dev;
1635
1636     buflen = *rbuflen - sizeof( bitmap );
1637     if (( ret = getvolparams( bitmap, vol, &st,
1638                               rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) {
1639         *rbuflen = 0;
1640         return( ret );
1641     }
1642     *rbuflen = buflen + sizeof( bitmap );
1643     bitmap = htons( bitmap );
1644     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1645     return( AFP_OK );
1646
1647 }
1648
1649 /* ------------------------------- */
1650 void load_volumes(AFPObj *obj)
1651 {
1652     struct passwd   *pwent;
1653
1654     if (Volumes) {
1655         int changed = 0;
1656
1657         /* check files date */
1658         if (obj->options.defaultvol.loaded) {
1659             changed = volfile_changed(&obj->options.defaultvol);
1660         }
1661         if (obj->options.systemvol.loaded) {
1662             changed |= volfile_changed(&obj->options.systemvol);
1663         }
1664         if (obj->options.uservol.loaded) {
1665             changed |= volfile_changed(&obj->options.uservol);
1666         }
1667         if (!changed)
1668             return;
1669
1670         free_extmap();
1671         free_volumes();
1672     }
1673
1674     pwent = getpwnam(obj->username);
1675     if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
1676         readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent);
1677     }
1678
1679     if ((*obj->username == '\0') || (obj->options.flags & OPTION_NOUSERVOL)) {
1680         readvolfile(obj, &obj->options.defaultvol, NULL, 1, pwent);
1681     } else if (pwent) {
1682         /*
1683          * Read user's AppleVolumes or .AppleVolumes file
1684          * If neither are readable, read the default volumes file. if
1685          * that doesn't work, create a user share.
1686          */
1687         if (obj->options.uservol.name) {
1688             free(obj->options.uservol.name);
1689         }
1690         obj->options.uservol.name = strdup(pwent->pw_dir);
1691         if ( readvolfile(obj, &obj->options.uservol,    "AppleVolumes", 1, pwent) < 0 &&
1692              readvolfile(obj, &obj->options.uservol, ".AppleVolumes", 1, pwent) < 0 &&
1693              readvolfile(obj, &obj->options.uservol, "applevolumes", 1, pwent) < 0 &&
1694              readvolfile(obj, &obj->options.uservol, ".applevolumes", 1, pwent) < 0 &&
1695              obj->options.defaultvol.name != NULL ) {
1696             if (readvolfile(obj, &obj->options.defaultvol, NULL, 1, pwent) < 0)
1697                 creatvol(obj, pwent, pwent->pw_dir, NULL, NULL, 1);
1698         }
1699     }
1700     if ( obj->options.flags & OPTION_USERVOLFIRST ) {
1701         readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent );
1702     }
1703
1704     if ( obj->options.closevol ) {
1705         struct vol *vol;
1706
1707         for ( vol = Volumes; vol; vol = vol->v_next ) {
1708             if (vol->v_deleted && !vol->v_new ) {
1709                 of_closevol(vol);
1710                 deletevol(vol);
1711                 vol = Volumes;
1712             }
1713         }
1714     }
1715 }
1716
1717 /* ------------------------------- */
1718 int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1719 {
1720     struct timeval  tv;
1721     struct stat     st;
1722     struct vol      *volume;
1723     char    *data;
1724     char        *namebuf;
1725     int         vcnt;
1726     size_t      len;
1727
1728     load_volumes(obj);
1729
1730     data = rbuf + 5;
1731     for ( vcnt = 0, volume = Volumes; volume; volume = volume->v_next ) {
1732         if (!(volume->v_flags & AFPVOL_NOSTAT)) {
1733             struct maccess ma;
1734
1735             if ( stat( volume->v_path, &st ) < 0 ) {
1736                 LOG(log_info, logtype_afpd, "afp_getsrvrparms(%s): stat: %s",
1737                     volume->v_path, strerror(errno) );
1738                 continue;       /* can't access directory */
1739             }
1740             if (!S_ISDIR(st.st_mode)) {
1741                 continue;       /* not a dir */
1742             }
1743             accessmode(volume->v_path, &ma, NULL, &st);
1744             if ((ma.ma_user & (AR_UREAD | AR_USEARCH)) != (AR_UREAD | AR_USEARCH)) {
1745                 continue;   /* no r-x access */
1746             }
1747         }
1748         if (volume->v_hide) {
1749             continue;       /* config file changed but the volume was mounted */
1750         }
1751
1752         if (utf8_encoding()) {
1753             len = ucs2_to_charset_allocate(CH_UTF8_MAC, &namebuf, volume->v_u8mname);
1754         } else {
1755             len = ucs2_to_charset_allocate(obj->options.maccharset, &namebuf, volume->v_macname);
1756         }
1757
1758         if (len == (size_t)-1)
1759             continue;
1760
1761         /* set password bit if there's a volume password */
1762         *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
1763
1764         /* Apple 2 clients running ProDOS-8 expect one volume to have
1765            bit 0 of this byte set.  They will not recognize anything
1766            on the server unless this is the case.  I have not
1767            completely worked this out, but it's related to booting
1768            from the server.  Support for that function is a ways
1769            off.. <shirsch@ibm.net> */
1770         *data |= (volume->v_flags & AFPVOL_A2VOL) ? AFPSRVR_CONFIGINFO : 0;
1771         *data++ |= 0; /* UNIX PRIVS BIT ..., OSX doesn't seem to use it, so we don't either */
1772         *data++ = len;
1773         memcpy(data, namebuf, len );
1774         data += len;
1775         free(namebuf);
1776         vcnt++;
1777     }
1778
1779     *rbuflen = data - rbuf;
1780     data = rbuf;
1781     if ( gettimeofday( &tv, NULL ) < 0 ) {
1782         LOG(log_error, logtype_afpd, "afp_getsrvrparms(%s): gettimeofday: %s", volume->v_path, strerror(errno) );
1783         *rbuflen = 0;
1784         return AFPERR_PARAM;
1785     }
1786     tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
1787     memcpy(data, &tv.tv_sec, sizeof( u_int32_t));
1788     data += sizeof( u_int32_t);
1789     *data = vcnt;
1790     return( AFP_OK );
1791 }
1792
1793 /* ------------------------- */
1794 static int volume_codepage(AFPObj *obj, struct vol *volume)
1795 {
1796     struct charset_functions *charset;
1797     /* Codepages */
1798
1799     if (!volume->v_volcodepage)
1800         volume->v_volcodepage = strdup("UTF8");
1801
1802     if ( (charset_t) -1 == ( volume->v_volcharset = add_charset(volume->v_volcodepage)) ) {
1803         LOG (log_error, logtype_afpd, "Setting codepage %s as volume codepage failed", volume->v_volcodepage);
1804         return -1;
1805     }
1806
1807     if ( NULL == (charset = find_charset_functions(volume->v_volcodepage)) || charset->flags & CHARSET_ICONV ) {
1808         LOG (log_warning, logtype_afpd, "WARNING: volume encoding %s is *not* supported by netatalk, expect problems !!!!", volume->v_volcodepage);
1809     }
1810
1811     if (!volume->v_maccodepage)
1812         volume->v_maccodepage = strdup(obj->options.maccodepage);
1813
1814     if ( (charset_t) -1 == ( volume->v_maccharset = add_charset(volume->v_maccodepage)) ) {
1815         LOG (log_error, logtype_afpd, "Setting codepage %s as mac codepage failed", volume->v_maccodepage);
1816         return -1;
1817     }
1818
1819     if ( NULL == ( charset = find_charset_functions(volume->v_maccodepage)) || ! (charset->flags & CHARSET_CLIENT) ) {
1820         LOG (log_error, logtype_afpd, "Fatal error: mac charset %s not supported", volume->v_maccodepage);
1821         return -1;
1822     }
1823     volume->v_kTextEncoding = htonl(charset->kTextEncoding);
1824     return 0;
1825 }
1826
1827 /* ------------------------- */
1828 static int volume_openDB(struct vol *volume)
1829 {
1830     int flags = 0;
1831
1832     if ((volume->v_flags & AFPVOL_NODEV)) {
1833         flags |= CNID_FLAG_NODEV;
1834     }
1835
1836     if (volume->v_cnidscheme == NULL) {
1837         volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME);
1838         LOG(log_info, logtype_afpd, "Volume %s use CNID scheme %s.", volume->v_path, volume->v_cnidscheme);
1839     }
1840
1841     LOG(log_info, logtype_afpd, "%s:%s", volume->v_cnidserver, volume->v_cnidport);
1842     
1843     volume->v_cdb = cnid_open(volume->v_dbpath ? volume->v_dbpath : volume->v_path,
1844                               volume->v_umask,
1845                               volume->v_cnidscheme,
1846                               flags,
1847                               volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
1848                               volume->v_cnidport ? volume->v_cnidport : Cnid_port);
1849
1850     if (!volume->v_cdb) {
1851         flags |= CNID_FLAG_MEMORY;
1852         LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.", volume->v_path);
1853         volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
1854 #ifdef SERVERTEXT
1855         /* kill ourself with SIGUSR2 aka msg pending */
1856         if (volume->v_cdb) {
1857             setmessage("Something wrong with the volume's DB ... FIXME with a better msg");
1858             kill(getpid(), SIGUSR2);
1859             /* XXX desactivate cachecnid ? */
1860         }
1861 #endif
1862     }
1863
1864     return (!volume->v_cdb)?-1:0;
1865 }
1866
1867 /* 
1868    Check if the underlying filesystem supports EAs for ea:sys volumes.
1869    If not, switch to ea:ad.
1870    As we can't check (requires write access) on ro-volumes, we switch ea:auto
1871    volumes that are options:ro to ea:none.
1872 */
1873 static void check_ea_sys_support(struct vol *vol)
1874 {
1875     uid_t process_uid = 0;
1876     char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
1877     const char *eacontent = "yes";
1878
1879     if (vol->v_vfs_ea == AFPVOL_EA_AUTO) {
1880
1881         if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
1882             LOG(log_info, logtype_logger, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname);
1883             vol->v_vfs_ea = AFPVOL_EA_NONE;
1884             return;
1885         }
1886
1887         mktemp(eaname);
1888
1889         process_uid = geteuid();
1890         if (process_uid)
1891             if (seteuid(0) == -1) {
1892                 LOG(log_error, logtype_logger, "check_ea_sys_support: can't seteuid(0): %s", strerror(errno));
1893                 exit(EXITERR_SYS);
1894             }
1895
1896         if ((sys_setxattr(vol->v_path, eaname, eacontent, 4, 0)) == 0) {
1897             sys_removexattr(vol->v_path, eaname);
1898             vol->v_vfs_ea = AFPVOL_EA_SYS;
1899         } else {
1900             LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes, using ea:ad instead",
1901                 vol->v_localname);
1902             vol->v_vfs_ea = AFPVOL_EA_AD;
1903         }
1904
1905         if (process_uid) {
1906             if (seteuid(process_uid) == -1) {
1907                 LOG(log_error, logtype_logger, "can't seteuid back %s", strerror(errno));
1908                 exit(EXITERR_SYS);
1909             }
1910         }
1911     }
1912 }
1913
1914 /* -------------------------
1915  * we are the user here
1916  */
1917 int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1918 {
1919     struct stat st;
1920     char    *volname;
1921     char        *p;
1922
1923     struct vol  *volume;
1924     struct dir  *dir;
1925     int     len, ret;
1926     size_t  namelen;
1927     u_int16_t   bitmap;
1928     char        path[ MAXPATHLEN + 1];
1929     char        *vol_uname;
1930     char        *vol_mname;
1931     char        *volname_tmp;
1932
1933     ibuf += 2;
1934     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1935     bitmap = ntohs( bitmap );
1936     ibuf += sizeof( bitmap );
1937
1938     *rbuflen = 0;
1939     if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
1940         return AFPERR_BITMAP;
1941     }
1942
1943     len = (unsigned char)*ibuf++;
1944     volname = obj->oldtmp;
1945
1946     if ((volname_tmp = strchr(volname,'+')) != NULL)
1947         volname = volname_tmp+1;
1948
1949     if (utf8_encoding()) {
1950         namelen = convert_string(CH_UTF8_MAC, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp));
1951     } else {
1952         namelen = convert_string(obj->options.maccharset, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp));
1953     }
1954
1955     if ( namelen <= 0) {
1956         return AFPERR_PARAM;
1957     }
1958
1959     ibuf += len;
1960     if ((len + 1) & 1) /* pad to an even boundary */
1961         ibuf++;
1962
1963     load_volumes(obj);
1964
1965     for ( volume = Volumes; volume; volume = volume->v_next ) {
1966         if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) {
1967             break;
1968         }
1969     }
1970
1971     if ( volume == NULL ) {
1972         return AFPERR_PARAM;
1973     }
1974
1975     /* check for a volume password */
1976     if (volume->v_password && strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
1977         return AFPERR_ACCESS;
1978     }
1979
1980     if (( volume->v_flags & AFPVOL_OPEN  ) ) {
1981         /* the volume is already open */
1982 #ifdef FORCE_UIDGID
1983         set_uidgid ( volume );
1984 #endif
1985         return stat_vol(bitmap, volume, rbuf, rbuflen);
1986     }
1987
1988     if (volume->v_root_preexec) {
1989         if ((ret = afprun(1, volume->v_root_preexec, NULL)) && volume->v_root_preexec_close) {
1990             LOG(log_error, logtype_afpd, "afp_openvol(%s): root preexec : %d", volume->v_path, ret );
1991             return AFPERR_MISC;
1992         }
1993     }
1994
1995 #ifdef FORCE_UIDGID
1996     set_uidgid ( volume );
1997 #endif
1998
1999     if (volume->v_preexec) {
2000         if ((ret = afprun(0, volume->v_preexec, NULL)) && volume->v_preexec_close) {
2001             LOG(log_error, logtype_afpd, "afp_openvol(%s): preexec : %d", volume->v_path, ret );
2002             return AFPERR_MISC;
2003         }
2004     }
2005
2006     if ( stat( volume->v_path, &st ) < 0 ) {
2007         return AFPERR_PARAM;
2008     }
2009
2010     if ( chdir( volume->v_path ) < 0 ) {
2011         return AFPERR_PARAM;
2012     }
2013
2014     if ( NULL == getcwd(path, MAXPATHLEN)) {
2015         /* shouldn't be fatal but it will fail later */
2016         LOG(log_error, logtype_afpd, "afp_openvol(%s): volume pathlen too long", volume->v_path);
2017         return AFPERR_MISC;
2018     }
2019
2020     /* Normalize volume path */
2021 #ifdef REALPATH_TAKES_NULL
2022     if ((volume->v_path = realpath(path, NULL)) == NULL)
2023         return AFPERR_MISC;
2024 #else
2025     if ((volume->v_path = malloc(MAXPATHLEN+1)) == NULL)
2026         return AFPERR_MISC;
2027     if (realpath(path, volume->v_path) == NULL) {
2028         free(volume->v_path);
2029         return AFPERR_MISC;
2030     }
2031     /* Safe some memory */
2032     char *tmp;
2033     if ((tmp = strdup(volume->v_path)) == NULL) {
2034         free(volume->v_path);
2035         return AFPERR_MISC;
2036     } 
2037     free(volume->v_path);
2038     volume->v_path = tmp;
2039 #endif
2040
2041     if (volume_codepage(obj, volume) < 0) {
2042         ret = AFPERR_MISC;
2043         goto openvol_err;
2044     }
2045
2046     /* initialize volume variables
2047      * FIXME file size
2048      */
2049     if (utf8_encoding()) {
2050         volume->max_filename = 255;
2051     }
2052     else {
2053         volume->max_filename = MACFILELEN;
2054     }
2055
2056     volume->v_dir = volume->v_root = NULL;
2057     volume->v_hash = NULL;
2058
2059     volume->v_flags |= AFPVOL_OPEN;
2060     volume->v_cdb = NULL;
2061
2062     if (utf8_encoding()) {
2063         len = convert_string_allocate(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, namelen, &vol_mname);
2064     } else {
2065         len = convert_string_allocate(CH_UCS2, obj->options.maccharset, volume->v_macname, namelen, &vol_mname);
2066     }
2067     if ( !vol_mname || len <= 0) {
2068         ret = AFPERR_MISC;
2069         goto openvol_err;
2070     }
2071
2072     if ((vol_uname = strrchr(path, '/')) == NULL)
2073         vol_uname = path;
2074     else if (*(vol_uname + 1) != '\0')
2075         vol_uname++;
2076
2077     if ((dir = dirnew(vol_mname, vol_uname) ) == NULL) {
2078         free(vol_mname);
2079         LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
2080         ret = AFPERR_MISC;
2081         goto openvol_err;
2082     }
2083     free(vol_mname);
2084
2085     dir->d_did = DIRDID_ROOT;
2086     dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
2087     dir->d_m_name_ucs2 = strdup_w(volume->v_name);
2088     volume->v_dir = volume->v_root = dir;
2089     volume->v_curdir = NULL;
2090     volume->v_hash = dirhash();
2091
2092     curdir = volume->v_dir;
2093     if (volume_openDB(volume) < 0) {
2094         LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s",
2095             volume->v_path, volume->v_cnidscheme);
2096         ret = AFPERR_MISC;
2097         goto openvol_err;
2098     }
2099
2100     ret  = stat_vol(bitmap, volume, rbuf, rbuflen);
2101     if (ret == AFP_OK) {
2102
2103         if (!(volume->v_flags & AFPVOL_RO)) {
2104             handle_special_folders( volume );
2105             savevolinfo(volume,
2106                         volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
2107                         volume->v_cnidport   ? volume->v_cnidport   : Cnid_port);
2108         }
2109
2110         /*
2111          * If you mount a volume twice, the second time the trash appears on
2112          * the desk-top.  That's because the Mac remembers the DID for the
2113          * trash (even for volumes in different zones, on different servers).
2114          * Just so this works better, we prime the DID cache with the trash,
2115          * fixing the trash at DID 17.
2116          * FIXME (RL): should it be done inside a CNID backend ? (always returning Trash DID when asked) ?
2117          */
2118         if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2119
2120             /* FIXME find db time stamp */
2121             if (cnid_getstamp(volume->v_cdb, volume->v_stamp, sizeof(volume->v_stamp)) < 0) {
2122                 LOG (log_error, logtype_afpd,
2123                      "afp_openvol(%s): Fatal error: Unable to get stamp value from CNID backend",
2124                      volume->v_path);
2125                 ret = AFPERR_MISC;
2126                 goto openvol_err;
2127             }
2128         }
2129         else {
2130             p = Trash;
2131             cname( volume, volume->v_dir, &p );
2132         }
2133         return( AFP_OK );
2134     }
2135
2136 openvol_err:
2137     if (volume->v_dir) {
2138         hash_free( volume->v_hash);
2139         dirfree( volume->v_dir );
2140         volume->v_dir = volume->v_root = NULL;
2141     }
2142
2143     volume->v_flags &= ~AFPVOL_OPEN;
2144     if (volume->v_cdb != NULL) {
2145         cnid_close(volume->v_cdb);
2146         volume->v_cdb = NULL;
2147     }
2148     *rbuflen = 0;
2149     return ret;
2150 }
2151
2152 /* ------------------------- */
2153 static void closevol(struct vol *vol)
2154 {
2155     if (!vol)
2156         return;
2157
2158     hash_free( vol->v_hash);
2159     dirfree( vol->v_root );
2160     vol->v_dir = NULL;
2161     if (vol->v_cdb != NULL) {
2162         cnid_close(vol->v_cdb);
2163         vol->v_cdb = NULL;
2164     }
2165
2166     if (vol->v_postexec) {
2167         afprun(0, vol->v_postexec, NULL);
2168     }
2169     if (vol->v_root_postexec) {
2170         afprun(1, vol->v_root_postexec, NULL);
2171     }
2172 }
2173
2174 /* ------------------------- */
2175 void close_all_vol(void)
2176 {
2177     struct vol  *ovol;
2178     curdir = NULL;
2179     for ( ovol = Volumes; ovol; ovol = ovol->v_next ) {
2180         if ( (ovol->v_flags & AFPVOL_OPEN) ) {
2181             ovol->v_flags &= ~AFPVOL_OPEN;
2182             closevol(ovol);
2183         }
2184     }
2185 }
2186
2187 /* ------------------------- */
2188 static void deletevol(struct vol *vol)
2189 {
2190     struct vol  *ovol;
2191
2192     vol->v_flags &= ~AFPVOL_OPEN;
2193     for ( ovol = Volumes; ovol; ovol = ovol->v_next ) {
2194         if ( (ovol->v_flags & AFPVOL_OPEN) ) {
2195             break;
2196         }
2197     }
2198     if ( ovol != NULL ) {
2199         /* Even if chdir fails, we can't say afp_closevol fails. */
2200         if ( chdir( ovol->v_path ) == 0 ) {
2201             curdir = ovol->v_dir;
2202         }
2203     }
2204
2205     closevol(vol);
2206     if (vol->v_deleted) {
2207         showvol(vol->v_name);
2208         volume_free(vol);
2209         volume_unlink(vol);
2210         free(vol);
2211     }
2212 }
2213
2214 /* ------------------------- */
2215 int afp_closevol(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2216 {
2217     struct vol  *vol;
2218     u_int16_t   vid;
2219
2220     *rbuflen = 0;
2221     ibuf += 2;
2222     memcpy(&vid, ibuf, sizeof( vid ));
2223     if (NULL == ( vol = getvolbyvid( vid )) ) {
2224         return( AFPERR_PARAM );
2225     }
2226
2227     deletevol(vol);
2228
2229     return( AFP_OK );
2230 }
2231
2232 /* ------------------------- */
2233 struct vol *getvolbyvid(const u_int16_t vid )
2234 {
2235     struct vol  *vol;
2236
2237     for ( vol = Volumes; vol; vol = vol->v_next ) {
2238         if ( vid == vol->v_vid ) {
2239             break;
2240         }
2241     }
2242     if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
2243         return( NULL );
2244     }
2245
2246 #ifdef FORCE_UIDGID
2247     set_uidgid ( vol );
2248 #endif /* FORCE_UIDGID */
2249
2250     return( vol );
2251 }
2252
2253 /* ------------------------ */
2254 static int ext_cmp_key(const void *key, const void *obj)
2255 {
2256     const char          *p = key;
2257     const struct extmap *em = obj;
2258     return strdiacasecmp(p, em->em_ext);
2259 }
2260 struct extmap *getextmap(const char *path)
2261 {
2262     char      *p;
2263     struct extmap *em;
2264
2265     if (!Extmap_cnt || NULL == ( p = strrchr( path, '.' )) ) {
2266         return( Defextmap );
2267     }
2268     p++;
2269     if (!*p) {
2270         return( Defextmap );
2271     }
2272     em = bsearch(p, Extmap, Extmap_cnt, sizeof(struct extmap), ext_cmp_key);
2273     if (em) {
2274         return( em );
2275     } else {
2276         return( Defextmap );
2277     }
2278 }
2279
2280 /* ------------------------- */
2281 struct extmap *getdefextmap(void)
2282 {
2283     return( Defextmap );
2284 }
2285
2286 /* --------------------------
2287    poll if a volume is changed by other processes.
2288    return
2289    0 no attention msg sent
2290    1 attention msg sent
2291    -1 error (socket closed)
2292
2293    Note: if attention return -1 no packet has been
2294    sent because the buffer is full, we don't care
2295    either there's no reader or there's a lot of
2296    traffic and another pollvoltime will follow
2297 */
2298 int  pollvoltime(AFPObj *obj)
2299
2300 {
2301     struct vol       *vol;
2302     struct timeval   tv;
2303     struct stat      st;
2304
2305     if (!(afp_version > 21 && obj->options.server_notif))
2306         return 0;
2307
2308     if ( gettimeofday( &tv, NULL ) < 0 )
2309         return 0;
2310
2311     for ( vol = Volumes; vol; vol = vol->v_next ) {
2312         if ( (vol->v_flags & AFPVOL_OPEN)  && vol->v_mtime + 30 < tv.tv_sec) {
2313             if ( !stat( vol->v_path, &st ) && vol->v_mtime != st.st_mtime ) {
2314                 vol->v_mtime = st.st_mtime;
2315                 if (!obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED))
2316                     return -1;
2317                 return 1;
2318             }
2319         }
2320     }
2321     return 0;
2322 }
2323
2324 /* ------------------------- */
2325 void setvoltime(AFPObj *obj, struct vol *vol)
2326 {
2327     struct timeval  tv;
2328
2329     /* just looking at vol->v_mtime is broken seriously since updates
2330      * from other users afpd processes never are seen.
2331      * This is not the most elegant solution (a shared memory between
2332      * the afpd processes would come closer)
2333      * [RS] */
2334
2335     if ( gettimeofday( &tv, NULL ) < 0 ) {
2336         LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) );
2337         return;
2338     }
2339     if( utime( vol->v_path, NULL ) < 0 ) {
2340         /* write of time failed ... probably a read only filesys,
2341          * where no other users can interfere, so there's no issue here
2342          */
2343     }
2344
2345     /* a little granularity */
2346     if (vol->v_mtime < tv.tv_sec) {
2347         vol->v_mtime = tv.tv_sec;
2348         /* or finder doesn't update free space
2349          * AFP 3.2 and above clients seem to be ok without so many notification
2350          */
2351         if (afp_version < 32 && obj->options.server_notif) {
2352             obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
2353         }
2354     }
2355 }
2356
2357 /* ------------------------- */
2358 int afp_getvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,char *rbuf, size_t *rbuflen)
2359 {
2360     struct vol  *vol;
2361     u_int16_t   vid, bitmap;
2362
2363     ibuf += 2;
2364     memcpy(&vid, ibuf, sizeof( vid ));
2365     ibuf += sizeof( vid );
2366     memcpy(&bitmap, ibuf, sizeof( bitmap ));
2367     bitmap = ntohs( bitmap );
2368
2369     if (NULL == ( vol = getvolbyvid( vid )) ) {
2370         *rbuflen = 0;
2371         return( AFPERR_PARAM );
2372     }
2373
2374     return stat_vol(bitmap, vol, rbuf, rbuflen);
2375 }
2376
2377 /* ------------------------- */
2378 int afp_setvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,  size_t *rbuflen)
2379 {
2380     struct adouble ad;
2381     struct vol  *vol;
2382     u_int16_t   vid, bitmap;
2383     u_int32_t   aint;
2384
2385     ibuf += 2;
2386     *rbuflen = 0;
2387
2388     memcpy(&vid, ibuf, sizeof( vid ));
2389     ibuf += sizeof( vid );
2390     memcpy(&bitmap, ibuf, sizeof( bitmap ));
2391     bitmap = ntohs( bitmap );
2392     ibuf += sizeof(bitmap);
2393
2394     if (( vol = getvolbyvid( vid )) == NULL ) {
2395         return( AFPERR_PARAM );
2396     }
2397
2398     if ((vol->v_flags & AFPVOL_RO))
2399         return AFPERR_VLOCK;
2400
2401     /* we can only set the backup date. */
2402     if (bitmap != (1 << VOLPBIT_BDATE))
2403         return AFPERR_BITMAP;
2404
2405     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2406     if ( ad_open( vol->v_path, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR,
2407                   0666, &ad) < 0 ) {
2408         if (errno == EROFS)
2409             return AFPERR_VLOCK;
2410
2411         return AFPERR_ACCESS;
2412     }
2413
2414     memcpy(&aint, ibuf, sizeof(aint));
2415     ad_setdate(&ad, AD_DATE_BACKUP, aint);
2416     ad_flush(&ad);
2417     ad_close(&ad, ADFLAGS_HF);
2418     return( AFP_OK );
2419 }
2420
2421 /* ------------------------- */
2422 int wincheck(const struct vol *vol, const char *path)
2423 {
2424     int len;
2425
2426     if (!(vol->v_flags & AFPVOL_MSWINDOWS))
2427         return 1;
2428
2429     /* empty paths are not allowed */
2430     if ((len = strlen(path)) == 0)
2431         return 0;
2432
2433     /* leading or trailing whitespaces are not allowed, carriage returns
2434      * and probably other whitespace is okay, tabs are not allowed
2435      */
2436     if ((path[0] == ' ') || (path[len-1] == ' '))
2437         return 0;
2438
2439     /* certain characters are not allowed */
2440     if (strpbrk(path, MSWINDOWS_BADCHARS))
2441         return 0;
2442
2443     /* everything else is okay */
2444     return 1;
2445 }
2446
2447
2448 /*
2449  * precreate a folder
2450  * this is only intended for folders in the volume root
2451  * It will *not* work if the folder name contains extended characters
2452  */
2453 static int create_special_folder (const struct vol *vol, const struct _special_folder *folder)
2454 {
2455     char        *p,*q,*r;
2456     struct adouble  ad;
2457     u_int16_t   attr;
2458     struct stat st;
2459     int     ret;
2460
2461
2462     p = (char *) malloc ( strlen(vol->v_path)+strlen(folder->name)+2);
2463     if ( p == NULL) {
2464         LOG(log_error, logtype_afpd,"malloc failed");
2465         exit (EXITERR_SYS);
2466     }
2467
2468     q=strdup(folder->name);
2469     if ( q == NULL) {
2470         LOG(log_error, logtype_afpd,"malloc failed");
2471         exit (EXITERR_SYS);
2472     }
2473
2474     strcpy(p, vol->v_path);
2475     strcat(p, "/");
2476
2477     r=q;
2478     while (*r) {
2479         if ((vol->v_casefold & AFPVOL_MTOUUPPER))
2480             *r=toupper(*r);
2481         else if ((vol->v_casefold & AFPVOL_MTOULOWER))
2482             *r=tolower(*r);
2483         r++;
2484     }
2485     strcat(p, q);
2486
2487     if ( (ret = stat( p, &st )) < 0 ) {
2488         if (folder->precreate) {
2489             if (ad_mkdir(p, folder->mode)) {
2490                 LOG(log_debug, logtype_afpd,"Creating '%s' failed in %s: %s", p, vol->v_path, strerror(errno));
2491                 free(p);
2492                 free(q);
2493                 return -1;
2494             }
2495             ret = 0;
2496         }
2497     }
2498
2499     if ( !ret && folder->hide) {
2500         /* Hide it */
2501         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2502         if (ad_open_metadata( p, ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2503             free (p);
2504             free(q);
2505             return (-1);
2506         }
2507         
2508         ad_setname(&ad, folder->name);
2509
2510         ad_getattr(&ad, &attr);
2511         attr |= htons( ntohs( attr ) | ATTRBIT_INVISIBLE );
2512         ad_setattr(&ad, attr);
2513
2514         /* do the same with the finder info */
2515         if (ad_entry(&ad, ADEID_FINDERI)) {
2516             memcpy(&attr, ad_entry(&ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, sizeof(attr));
2517             attr   |= htons(FINDERINFO_INVISIBLE);
2518             memcpy(ad_entry(&ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,&attr, sizeof(attr));
2519         }
2520
2521         ad_flush( &ad );
2522         ad_close_metadata( &ad);
2523     }
2524     free(p);
2525     free(q);
2526     return 0;
2527 }
2528
2529 static void handle_special_folders (const struct vol * vol)
2530 {
2531     const _special_folder *p = &special_folders[0];
2532
2533     if ((vol->v_flags & AFPVOL_RO))
2534         return;
2535
2536     for (; p->name != NULL; p++) {
2537         create_special_folder (vol, p);
2538     }
2539 }
2540