• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.5/libatalk/adouble/
1/*
2 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
3 * Copyright (c) 1990,1991 Regents of The University of Michigan.
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and distribute this software and
7 * its documentation for any purpose and without fee is hereby granted,
8 * provided that the above copyright notice appears in all copies and
9 * that both that copyright notice and this permission notice appear
10 * in supporting documentation, and that the name of The University
11 * of Michigan not be used in advertising or publicity pertaining to
12 * distribution of the software without specific, written prior
13 * permission. This software is supplied as is without expressed or
14 * implied warranties of any kind.
15 *
16 *  Research Systems Unix Group
17 *  The University of Michigan
18 *  c/o Mike Clark
19 *  535 W. William Street
20 *  Ann Arbor, Michigan
21 *  +1-313-763-0525
22 *  netatalk@itd.umich.edu
23 *
24 */
25
26/*!
27 * @file
28 * Part of Netatalk's AppleDouble implementatation
29 * @note We don't use inlines because a good compiler should be
30 *       able to optimize all the static funcs below.
31 * @sa include/atalk/adouble.h
32 */
33
34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif /* HAVE_CONFIG_H */
37
38#include <errno.h>
39
40#include <atalk/adouble.h>
41#include <sys/param.h>
42#include <atalk/logger.h>
43
44#include <atalk/util.h>
45#include <string.h>
46
47#include "ad_private.h"
48#include <stdlib.h>
49
50#ifndef MAX
51#define MAX(a, b)  ((a) < (b) ? (b) : (a))
52#endif /* ! MAX */
53
54/*
55 * AppleDouble entry default offsets.
56 * The layout looks like this:
57 *
58 * this is the v1 layout:
59 *     255         200         16          32          N
60 *  |  NAME |    COMMENT    | FILEI |    FINDERI    | RFORK |
61 *
62 * we need to change it to look like this:
63 *
64 * v2 layout:
65 * field       length (in bytes)
66 * NAME        255
67 * COMMENT     200
68 * FILEDATESI  16     replaces FILEI
69 * FINDERI     32
70 * DID          4     new
71 * AFPFILEI     4     new
72 * SHORTNAME   12     8.3 new
73 * RFORK        N
74 *
75 * so, all we need to do is replace FILEI with FILEDATESI, move RFORK,
76 * and add in the new fields.
77 *
78 * NOTE: the HFS module will need similar modifications to interact with
79 * afpd correctly.
80 */
81
82#define ADEDOFF_MAGIC        (0)
83#define ADEDOFF_VERSION      (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
84#define ADEDOFF_FILLER       (ADEDOFF_VERSION + ADEDLEN_VERSION)
85#define ADEDOFF_NENTRIES     (ADEDOFF_FILLER + ADEDLEN_FILLER)
86
87/* initial lengths of some of the fields */
88#define ADEDLEN_INIT     0
89
90/* make sure we don't redefine ADEDOFF_FILEI */
91#ifdef ADEDOFF_FILEI
92#undef ADEDOFF_FILEI
93#endif /* ADEDOFF_FILEI */
94
95#define ADEDOFF_NAME_V1      (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
96#define ADEDOFF_COMMENT_V1   (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
97#define ADEDOFF_FILEI        (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
98#define ADEDOFF_FINDERI_V1   (ADEDOFF_FILEI + ADEDLEN_FILEI)
99#define ADEDOFF_RFORK_V1     (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
100
101/* i stick things in a slightly different order than their eid order in
102 * case i ever want to separate RootInfo behaviour from the rest of the
103 * stuff. */
104#define ADEDOFF_NAME_V2      (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
105#define ADEDOFF_COMMENT_V2   (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
106#define ADEDOFF_FILEDATESI   (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
107#define ADEDOFF_FINDERI_V2   (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
108#define ADEDOFF_DID      (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
109#define ADEDOFF_AFPFILEI     (ADEDOFF_DID + ADEDLEN_DID)
110#define ADEDOFF_SHORTNAME    (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
111#define ADEDOFF_PRODOSFILEI  (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
112#define ADEDOFF_PRIVDEV      (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
113#define ADEDOFF_PRIVINO      (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV)
114#define ADEDOFF_PRIVSYN      (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO)
115#define ADEDOFF_PRIVID       (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN)
116
117#define ADEDOFF_RFORK_V2     (ADEDOFF_PRIVID + ADEDLEN_PRIVID)
118
119#define ADEID_NUM_OSX        2
120#define ADEDOFF_FINDERI_OSX  (AD_HEADER_LEN + ADEID_NUM_OSX*AD_ENTRY_LEN)
121#define ADEDOFF_RFORK_OSX    (ADEDOFF_FINDERI_OSX + ADEDLEN_FINDERI)
122
123/* we keep local copies of a bunch of stuff so that we can initialize things
124 * correctly. */
125
126/* this is to prevent changing timezones from causing problems with
127   localtime volumes. the screw-up is 30 years. we use a delta of 5
128   years.  */
129#define TIMEWARP_DELTA 157680000
130
131
132struct entry {
133    u_int32_t id, offset, len;
134};
135
136static const struct entry entry_order1[ADEID_NUM_V1 +1] = {
137    {ADEID_NAME,    ADEDOFF_NAME_V1,    ADEDLEN_INIT},      /* 3 */
138    {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT},      /* 4 */
139    {ADEID_FILEI,   ADEDOFF_FILEI,      ADEDLEN_FILEI},     /* 7 */
140    {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI},   /* 9 */
141    {ADEID_RFORK,   ADEDOFF_RFORK_V1,   ADEDLEN_INIT},      /* 2 */
142    {0, 0, 0}
143};
144
145#if AD_VERSION == AD_VERSION1
146#define DISK_EID(ad, a) (a)
147
148#else /* AD_VERSION == AD_VERSION2 */
149
150static u_int32_t get_eid(struct adouble *ad, u_int32_t eid)
151{
152    if (eid <= 15)
153        return eid;
154    if (ad->ad_version == AD_VERSION1)
155        return 0;
156    if (eid == AD_DEV)
157        return ADEID_PRIVDEV;
158    if (eid == AD_INO)
159        return ADEID_PRIVINO;
160    if (eid == AD_SYN)
161        return ADEID_PRIVSYN;
162    if (eid == AD_ID)
163        return ADEID_PRIVID;
164
165    return 0;
166}
167
168#define DISK_EID(ad, a) get_eid(ad, a)
169
170static const struct entry entry_order2[ADEID_NUM_V2 +1] = {
171    {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
172    {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
173    {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
174    {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
175    {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
176    {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
177    {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
178    {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
179    {ADEID_PRIVDEV,     ADEDOFF_PRIVDEV, ADEDLEN_INIT},
180    {ADEID_PRIVINO,     ADEDOFF_PRIVINO, ADEDLEN_INIT},
181    {ADEID_PRIVSYN,     ADEDOFF_PRIVSYN, ADEDLEN_INIT},
182    {ADEID_PRIVID,     ADEDOFF_PRIVID, ADEDLEN_INIT},
183    {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
184
185    {0, 0, 0}
186};
187
188/* OS X adouble finder info and resource fork only
189 */
190static const struct entry entry_order_osx[ADEID_NUM_OSX +1] = {
191    {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI},
192    {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT},
193
194    {0, 0, 0}
195};
196
197#define ADEID_NUM_SFM 3
198static const struct entry entry_order_sfm[ADEID_NUM_SFM +1] = {
199    {ADEID_FINDERI,     16,         ADEDLEN_FINDERI},   /* 9 */
200    {ADEID_SFMRESERVE2, 16+32,      6},                 /* 21 */
201    {ADEID_FILEI,       60,         ADEDLEN_FILEI},     /* 7 */
202
203    {0, 0, 0}
204};
205
206#endif /* AD_VERSION == AD_VERSION2 */
207
208#if AD_VERSION == AD_VERSION2
209
210/* update a version 2 adouble resource fork with our private entries */
211static int ad_update(struct adouble *ad, const char *path)
212{
213    struct stat st;
214    u_int16_t nentries = 0;
215    off_t     off, shiftdata=0;
216    const struct entry  *eid;
217    static off_t entry_len[ADEID_MAX];
218    static char  databuf[ADEID_MAX][256], *buf;
219    int fd;
220    int ret = -1;
221
222    /* check to see if we should convert this header. */
223    if (!path || ad->ad_flags != AD_VERSION2)
224        return 0;
225
226    LOG(log_maxdebug, logtype_default, "ad_update: checking whether '%s' needs an upgrade.", path);
227
228    if (!(ad->ad_md->adf_flags & O_RDWR)) {
229        /* we were unable to open the file read write the last time */
230        return 0;
231    }
232
233    if (ad->ad_eid[ADEID_RFORK].ade_off) {
234        shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off;
235    }
236
237    memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof( nentries ));
238    nentries = ntohs( nentries );
239
240    if ( shiftdata == 0 && nentries == ADEID_NUM_V2)
241        return 0;
242
243    memset(entry_len, 0, sizeof(entry_len));
244    memset(databuf, 0, sizeof(databuf));
245
246    /* bail if we can't get a lock */
247    if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0)
248        goto bail_err;
249
250    fd = ad->ad_md->adf_fd;
251
252    if (fstat(fd, &st)) {
253        goto bail_lock;
254    }
255
256    if (st.st_size > 0x7fffffff) {
257        LOG(log_debug, logtype_default, "ad_update: file '%s' too big for update.", path);
258        errno = EIO;
259        goto bail_lock;
260    }
261
262    off = ad->ad_eid[ADEID_RFORK].ade_off;
263    if (off > st.st_size) {
264        LOG(log_error, logtype_default, "ad_update: invalid resource fork offset. (off: %u)", off);
265        errno = EIO;
266        goto bail_lock;
267    }
268
269    if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) {
270        LOG(log_error, logtype_default, "ad_update: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len);
271        errno = EIO;
272        goto bail_lock;
273    }
274
275    if ((void *) (buf = (char *)
276                  mmap(NULL, st.st_size + shiftdata,
277                       PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
278        MAP_FAILED) {
279        goto bail_lock;
280    }
281
282    /* last place for failure. */
283    if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
284        munmap(buf, st.st_size + shiftdata);
285        goto bail_lock;
286    }
287
288    /* move the RFORK. this assumes that the RFORK is at the end */
289    if (off) {
290        memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len);
291    }
292
293    munmap(buf, st.st_size + shiftdata);
294
295    /* now, fix up our copy of the header */
296    memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
297
298    /* save the header entries */
299    eid = entry_order2;
300    while (eid->id) {
301        if( ad->ad_eid[eid->id].ade_off != 0) {
302            if ( eid->id > 2 && ad->ad_eid[eid->id].ade_len < 256)
303                memcpy( databuf[eid->id], ad->ad_data +ad->ad_eid[eid->id].ade_off, ad->ad_eid[eid->id].ade_len);
304            entry_len[eid->id] = ad->ad_eid[eid->id].ade_len;
305        }
306        eid++;
307    }
308
309    memset(ad->ad_data + AD_HEADER_LEN, 0, AD_DATASZ - AD_HEADER_LEN);
310
311    /* copy the saved entries to the new header */
312    eid = entry_order2;
313    while (eid->id) {
314        if ( eid->id > 2 && entry_len[eid->id] > 0) {
315            memcpy(ad->ad_data+eid->offset, databuf[eid->id], entry_len[eid->id]);
316        }
317        ad->ad_eid[eid->id].ade_off = eid->offset;
318        ad->ad_eid[eid->id].ade_len = entry_len[eid->id];
319        eid++;
320    }
321
322    /* rebuild the header and cleanup */
323    LOG(log_debug, logtype_default, "updated AD2 header %s", path);
324    ad_flush(ad );
325    ret = 0;
326
327bail_lock:
328    ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
329bail_err:
330    return ret;
331}
332
333/* ------------------------------------------
334   FIXME work only if < 2GB
335*/
336static int ad_convert(struct adouble *ad, const char *path)
337{
338    struct stat st;
339    u_int16_t attr;
340    char *buf;
341    int fd, off;
342    int ret = -1;
343    /* use resource fork offset from file */
344    int shiftdata;
345    int toV2;
346    int toV1;
347
348    if (!path) {
349        return 0;
350    }
351
352    if (!(ad->ad_md->adf_flags & ( O_RDWR))) {
353        /* we were unable to open the file read write the last time */
354        return 0;
355    }
356
357    /* check to see if we should convert this header. */
358    toV2 = ad->ad_version == AD_VERSION1 && ad->ad_flags == AD_VERSION2;
359    toV1 = ad->ad_version == AD_VERSION2 && ad->ad_flags == AD_VERSION1;
360
361    if (!toV2 && !toV1)
362        return 0;
363
364    /* convert from v1 to v2. what does this mean?
365     *  1) change FILEI into FILEDATESI
366     *  2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
367     *  3) move FILEI attributes into AFPFILEI
368     *  4) initialize ACCESS field of FILEDATESI.
369     *  5) move the resource fork
370     */
371
372    /* bail if we can't get a lock */
373    if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0)
374        goto bail_err;
375
376    /* we reuse fd from the resource fork */
377    fd = ad->ad_md->adf_fd;
378
379    if (ad->ad_eid[ADEID_RFORK].ade_off) {
380        shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off;
381    }
382    else {
383        shiftdata = ADEDOFF_RFORK_V2 -ADEDOFF_RFORK_V1; /* 136 */
384    }
385
386    if (fstat(fd, &st)) {
387        goto bail_lock;
388    }
389
390    if (st.st_size > 0x7fffffff -shiftdata) {
391        LOG(log_debug, logtype_default, "ad_v1tov2: file too big.");
392        errno = EIO;
393        goto bail_lock;
394    }
395
396    off = ad->ad_eid[ADEID_RFORK].ade_off;
397
398    if (off > st.st_size) {
399        LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork offset. (off: %u)", off);
400        errno = EIO;
401        goto bail_lock;
402    }
403
404    if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) {
405        LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len);
406        errno = EIO;
407        goto bail_lock;
408    }
409
410    if ((void *) (buf = (char *)
411                  mmap(NULL, st.st_size + shiftdata,
412                       PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
413        MAP_FAILED) {
414        goto bail_lock;
415    }
416
417    /* last place for failure. */
418
419    if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
420        goto bail_lock;
421    }
422
423    /* move the RFORK. this assumes that the RFORK is at the end */
424    if (off) {
425        memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len);
426    }
427
428    munmap(buf, st.st_size + shiftdata);
429
430    /* now, fix up our copy of the header */
431    memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
432
433    /* replace FILEI with FILEDATESI */
434    ad_getattr(ad, &attr);
435    ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
436    ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
437    ad->ad_eid[ADEID_FILEI].ade_off = 0;
438    ad->ad_eid[ADEID_FILEI].ade_len = 0;
439
440    /* add in the new entries */
441    ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
442    ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
443    ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
444    ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
445    ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
446    ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
447    ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
448    ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
449
450    ad->ad_eid[ADEID_PRIVDEV].ade_off = ADEDOFF_PRIVDEV;
451    ad->ad_eid[ADEID_PRIVDEV].ade_len = ADEDLEN_INIT;
452    ad->ad_eid[ADEID_PRIVINO].ade_off = ADEDOFF_PRIVINO;
453    ad->ad_eid[ADEID_PRIVINO].ade_len = ADEDLEN_INIT;
454    ad->ad_eid[ADEID_PRIVSYN].ade_off = ADEDOFF_PRIVSYN;
455    ad->ad_eid[ADEID_PRIVSYN].ade_len = ADEDLEN_INIT;
456    ad->ad_eid[ADEID_PRIVID].ade_off  = ADEDOFF_PRIVID;
457    ad->ad_eid[ADEID_PRIVID].ade_len =  ADEDLEN_INIT;
458
459    /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
460    ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
461    ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
462    ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
463    ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
464
465    /* switch to dest version */
466    ad->ad_version = (toV2)?AD_VERSION2:AD_VERSION1;
467
468    /* move our data buffer to make space for the new entries. */
469    memmove(ad->ad_data + ADEDOFF_NAME_V2, ad->ad_data + ADEDOFF_NAME_V1,
470            ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
471
472    /* now, fill in the space with appropriate stuff. we're
473       operating as a v2 file now. */
474    ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
475    memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
476    memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
477    ad_setattr(ad, attr);
478    memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
479    memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
480
481    /* rebuild the header and cleanup */
482    ad_flush(ad );
483    ret = 0;
484
485bail_lock:
486    ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
487bail_err:
488    return ret;
489}
490#endif /* AD_VERSION == AD_VERSION2 */
491
492/* -------------------------------------
493   read in the entries
494*/
495static void parse_entries(struct adouble *ad, char *buf,
496                          u_int16_t nentries)
497{
498    u_int32_t   eid, len, off;
499    int         warning = 0;
500
501    /* now, read in the entry bits */
502    for (; nentries > 0; nentries-- ) {
503        memcpy(&eid, buf, sizeof( eid ));
504        eid = DISK_EID(ad, ntohl( eid ));
505        buf += sizeof( eid );
506        memcpy(&off, buf, sizeof( off ));
507        off = ntohl( off );
508        buf += sizeof( off );
509        memcpy(&len, buf, sizeof( len ));
510        len = ntohl( len );
511        buf += sizeof( len );
512
513        if (eid && eid < ADEID_MAX && off < sizeof(ad->ad_data) &&
514            (off +len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
515            ad->ad_eid[ eid ].ade_off = off;
516            ad->ad_eid[ eid ].ade_len = len;
517        } else if (!warning) {
518            warning = 1;
519            LOG(log_debug, logtype_default, "ad_refresh: nentries %hd  eid %d",
520                nentries, eid );
521        }
522    }
523}
524
525
526/* this reads enough of the header so that we can figure out all of
527 * the entry lengths and offsets. once that's done, we just read/mmap
528 * the rest of the header in.
529 *
530 * NOTE: we're assuming that the resource fork is kept at the end of
531 *       the file. also, mmapping won't work for the hfs fs until it
532 *       understands how to mmap header files. */
533static int ad_header_read(struct adouble *ad, struct stat *hst)
534{
535    char                *buf = ad->ad_data;
536    u_int16_t           nentries;
537    int                 len;
538    ssize_t             header_len;
539    static int          warning = 0;
540    struct stat         st;
541
542    /* read the header */
543    if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
544        return -1;
545    }
546    if (header_len < AD_HEADER_LEN) {
547        errno = EIO;
548        return -1;
549    }
550
551    memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
552    memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
553
554    /* tag broken v1 headers. just assume they're all right.
555     * we detect two cases: null magic/version
556     *                      byte swapped magic/version
557     * XXX: in the future, you'll need the v1compat flag.
558     * (ad->ad_flags & ADFLAGS_V1COMPAT) */
559    if (!ad->ad_magic && !ad->ad_version) {
560        if (!warning) {
561            LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
562            warning++;
563        }
564        ad->ad_magic = AD_MAGIC;
565        ad->ad_version = AD_VERSION1;
566
567    } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) {
568        if (!warning) {
569            LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
570            warning++;
571        }
572
573    } else {
574        ad->ad_magic = ntohl( ad->ad_magic );
575        ad->ad_version = ntohl( ad->ad_version );
576    }
577
578    if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
579#if AD_VERSION == AD_VERSION2
580                                       && (ad->ad_version != AD_VERSION2)
581#endif /* AD_VERSION == AD_VERSION2 */
582            )) {
583        LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
584        errno = EIO;
585        return -1;
586    }
587
588    memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
589    memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
590    nentries = ntohs( nentries );
591
592    /* read in all the entry headers. if we have more than the
593     * maximum, just hope that the rfork is specified early on. */
594    len = nentries*AD_ENTRY_LEN;
595
596    if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
597        len = sizeof(ad->ad_data) - AD_HEADER_LEN;
598
599    buf += AD_HEADER_LEN;
600    if (len > header_len - AD_HEADER_LEN) {
601        LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
602        errno = EIO;
603        return -1;
604    }
605
606    /* figure out all of the entry offsets and lengths. if we aren't
607     * able to read a resource fork entry, bail. */
608    nentries = len / AD_ENTRY_LEN;
609    parse_entries(ad, buf, nentries);
610    if (!ad_getentryoff(ad, ADEID_RFORK)
611        || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
612        ) {
613        LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset.");
614        errno = EIO;
615        return -1;
616    }
617
618    if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
619        LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
620        errno = EIO;
621        return -1;
622    }
623
624    if (hst == NULL) {
625        hst = &st;
626        if (fstat(ad->ad_md->adf_fd, &st) < 0) {
627            return 1; /* fail silently */
628        }
629    }
630    ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
631
632    /* fix up broken dates */
633    if (ad->ad_version == AD_VERSION1) {
634        u_int32_t aint;
635
636        /* check to see if the ad date is wrong. just see if we have
637         * a modification date in the future. */
638        if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
639            (aint > TIMEWARP_DELTA + hst->st_mtime)) {
640            ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
641            ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
642            ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
643            ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
644            ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
645        }
646    }
647
648    return 0;
649}
650
651/* ---------------------------
652   SFM structure
653*/
654#if 0
655typedef struct {
656    byte    afpi_Signature[4];      /* Must be 0x00504641 */
657    byte    afpi_Version[4];        /* Must be 0x00010000 */
658    byte    afpi_Reserved1[4];
659    byte    afpi_BackupTime[4];     /* Backup time for the file/dir */
660    byte    finderinfo[32];         /* Finder info */
661    byte    afpi_ProDosInfo[6];     /* ProDos Info */
662    byte    afpi_Reserved2[6];
663} sfm_info;
664#endif
665
666static int ad_header_sfm_read(struct adouble *ad, struct stat *hst)
667{
668    char                *buf = ad->ad_data;
669    const struct entry  *eid;
670    ssize_t             header_len;
671    struct stat         st;
672
673    /* read the header */
674    if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
675        return -1;
676    }
677    if (header_len != AD_SFM_LEN) {
678        errno = EIO;
679        return -1;
680    }
681
682    memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
683    memcpy(&ad->ad_version, buf + 4, sizeof( ad->ad_version ));
684
685    /* FIXME in the great Microsoft tradition they aren't in network order */
686#if 0
687    if (ad->ad_magic == SFM_MAGIC && ad->ad_version == AD_VERSION1) {
688        static int          warning = 0;
689        if (!warning) {
690            LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
691            warning++;
692        }
693
694    } else {
695        ad->ad_magic = ntohl( ad->ad_magic );
696        ad->ad_version = ntohl( ad->ad_version );
697    }
698#endif
699    if ((ad->ad_magic != SFM_MAGIC) || ((ad->ad_version != AD_VERSION1) )) {
700        errno = EIO;
701        LOG(log_debug, logtype_default, "ad_header_sfm_read: can't parse AppleDouble header.");
702        return -1;
703    }
704
705    /* reinit adouble table */
706    eid = entry_order_sfm;
707    while (eid->id) {
708        ad->ad_eid[eid->id].ade_off = eid->offset;
709        ad->ad_eid[eid->id].ade_len = eid->len;
710        eid++;
711    }
712
713    /* steal some prodos for attribute */
714    {
715
716        u_int16_t attribute;
717        memcpy(&attribute, buf + 48 +4, sizeof(attribute));
718        ad_setattr(ad, attribute );
719    }
720
721    if (ad->ad_resource_fork.adf_fd != -1) {
722        /* we have a resource fork use it rather than the metadata */
723        if (fstat(ad->ad_resource_fork.adf_fd, &st) < 0) {
724            /* XXX set to zero ?
725               ad->ad_rlen =  0;
726            */
727            return 1;
728        }
729        ad->ad_rlen = st.st_size;
730        hst = &st;
731    }
732    else if (hst == NULL) {
733        hst = &st;
734        if (fstat(ad->ad_md->adf_fd, &st) < 0) {
735            return 1; /* fail silently */
736        }
737    }
738
739    /* fix up broken dates */
740    if (ad->ad_version == AD_VERSION1) {
741        u_int32_t aint;
742
743        /* check to see if the ad date is wrong. just see if we have
744         * a modification date in the future. */
745        if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
746            (aint > TIMEWARP_DELTA + hst->st_mtime)) {
747            ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
748            ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
749            ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
750            ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
751            ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
752        }
753    }
754
755    return 0;
756}
757
758/* ---------------------------------------
759 * Put the .AppleDouble where it needs to be:
760 *
761 *      /   a/.AppleDouble/b
762 *  a/b
763 *      \   b/.AppleDouble/.Parent
764 *
765 * FIXME: should do something for pathname > MAXPATHLEN
766 */
767char *
768ad_path( const char *path, int adflags)
769{
770    static char pathbuf[ MAXPATHLEN + 1];
771    const char *slash;
772    size_t  l ;
773
774    if ( adflags & ADFLAGS_DIR ) {
775        l = strlcpy( pathbuf, path, sizeof(pathbuf));
776
777        if ( l && l < MAXPATHLEN) {
778            pathbuf[l++] = '/';
779        }
780        strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l);
781    } else {
782        if (NULL != ( slash = strrchr( path, '/' )) ) {
783            slash++;
784            l = slash - path;
785            /* XXX we must return NULL here and test in the caller */
786            if (l > MAXPATHLEN)
787                l = MAXPATHLEN;
788            memcpy( pathbuf, path, l);
789        } else {
790            l = 0;
791            slash = path;
792        }
793        l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l);
794        strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l);
795    }
796
797    return( pathbuf );
798}
799
800/* -------------------- */
801static int ad_mkrf(char *path)
802{
803    char *slash;
804    /*
805     * Probably .AppleDouble doesn't exist, try to mkdir it.
806     */
807    if (NULL == ( slash = strrchr( path, '/' )) ) {
808        return -1;
809    }
810    *slash = '\0';
811    errno = 0;
812    if ( ad_mkdir( path, 0777 ) < 0 ) {
813        return -1;
814    }
815    *slash = '/';
816    return 0;
817}
818
819/* ---------------------------------------
820 * Put the resource fork where it needs to be:
821 * ._name
822 */
823char *
824ad_path_osx(const char *path, int adflags _U_)
825{
826    static char pathbuf[ MAXPATHLEN + 1];
827    char    c, *slash, buf[MAXPATHLEN + 1];
828
829    if (!strcmp(path,".")) {
830        /* fixme */
831        getcwd(buf, MAXPATHLEN);
832    }
833    else {
834        strlcpy(buf, path, MAXPATHLEN +1);
835    }
836    if (NULL != ( slash = strrchr( buf, '/' )) ) {
837        c = *++slash;
838        *slash = '\0';
839        strlcpy( pathbuf, buf, MAXPATHLEN +1);
840        *slash = c;
841    } else {
842        pathbuf[ 0 ] = '\0';
843        slash = buf;
844    }
845    strlcat( pathbuf, "._", MAXPATHLEN  +1);
846    strlcat( pathbuf, slash, MAXPATHLEN +1);
847    return pathbuf;
848}
849/* -------------------- */
850static int ad_mkrf_osx(char *path _U_)
851{
852    return 0;
853}
854
855/* ---------------------------------------
856 * Put the .AppleDouble where it needs to be:
857 *
858 *      /   a/.AppleDouble/b/AFP_AfpInfo
859 *  a/b
860 *      \   b/.AppleDouble/.Parent/AFP_AfpInfo
861 *
862 */
863char *
864ad_path_sfm( const char *path, int adflags)
865{
866    static char pathbuf[ MAXPATHLEN + 1];
867    char    c, *slash, buf[MAXPATHLEN + 1];
868    size_t      l;
869
870    l = strlcpy(buf, path, MAXPATHLEN +1);
871    if ( adflags & ADFLAGS_DIR ) {
872        strcpy( pathbuf, buf);
873        if ( *buf != '\0' && l < MAXPATHLEN) {
874            pathbuf[l++] = '/';
875            pathbuf[l] = 0;
876        }
877        slash = ".Parent";
878    } else {
879        if (NULL != ( slash = strrchr( buf, '/' )) ) {
880            c = *++slash;
881            *slash = '\0';
882            strcpy( pathbuf, buf);
883            *slash = c;
884        } else {
885            pathbuf[ 0 ] = '\0';
886            slash = buf;
887        }
888    }
889    strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
890    strlcat( pathbuf, slash, MAXPATHLEN +1);
891
892    if ((adflags == ADFLAGS_RF)) {
893        strlcat( pathbuf, "/AFP_Resource", MAXPATHLEN +1);
894    }
895    else {
896        strlcat( pathbuf, "/AFP_AfpInfo", MAXPATHLEN +1);
897    }
898    return( pathbuf );
899}
900
901/* -------------------- */
902static int ad_mkrf_sfm(char *path)
903{
904    char *slash;
905    /*
906     * Probably .AppleDouble doesn't exist, try to mkdir it.
907     */
908    if (NULL == ( slash = strrchr( path, '/' )) ) {
909        return -1;
910    }
911    *slash = 0;
912    errno = 0;
913    if ( ad_mkdir( path, 0777 ) < 0 ) {
914        if ( errno == ENOENT ) {
915            char *slash1;
916
917            if (NULL == ( slash1 = strrchr( path, '/' )) )
918                return -1;
919            errno = 0;
920            *slash1 = 0;
921            if ( ad_mkdir( path, 0777 ) < 0 )
922                return -1;
923            *slash1 = '/';
924            if ( ad_mkdir( path, 0777 ) < 0 )
925                return -1;
926        }
927        else
928            return -1;
929    }
930    *slash = '/';
931    return 0;
932}
933
934/* -------------------------
935 * Support inherited protection modes for AppleDouble files.  The supplied
936 * mode is ANDed with the parent directory's mask value in lieu of "umask",
937 * and that value is returned.
938 */
939
940#define DEFMASK 07700   /* be conservative */
941
942char
943*ad_dir(const char *path)
944{
945    static char     modebuf[ MAXPATHLEN + 1];
946    char        *slash;
947    /*
948     * For a path with directories in it, remove the final component
949     * (path or subdirectory name) to get the name we want to stat.
950     * For a path which is just a filename, use "." instead.
951     */
952    slash = strrchr( path, '/' );
953    if (slash) {
954        size_t len;
955
956        len = slash - path;
957        if (len >= MAXPATHLEN) {
958            errno = ENAMETOOLONG;
959            return NULL;  /* can't do it */
960        }
961        memcpy( modebuf, path, len );
962        modebuf[len] = '\0';
963        /* is last char a '/' ? */
964        if (slash[1] == 0) {
965            slash = modebuf+ len;
966            /* remove them */
967            while (modebuf < slash && slash[-1] == '/') {
968                --slash;
969            }
970            if (modebuf == slash) {
971                goto use_cur;
972            }
973            *slash = '\0';
974            while (modebuf < slash && *slash != '/') {
975                --slash;
976            }
977            if (modebuf == slash) {
978                goto use_cur;
979            }
980            *slash = '\0';      /* remove pathname component */
981        }
982        return modebuf;
983    }
984use_cur:
985    modebuf[0] = '.';   /* use current directory */
986    modebuf[1] = '\0';
987    return modebuf;
988}
989
990/* ---------------- */
991static uid_t default_uid = -1;
992
993int ad_setfuid(const uid_t id)
994{
995    default_uid = id;
996    return 0;
997}
998
999/* ---------------- */
1000uid_t ad_getfuid(void)
1001{
1002    return default_uid;
1003}
1004
1005/* ----------------
1006   return inode of path parent directory
1007*/
1008int ad_stat(const char *path, struct stat *stbuf)
1009{
1010    char                *p;
1011
1012    p = ad_dir(path);
1013    if (!p) {
1014        return -1;
1015    }
1016
1017    return stat(p, stbuf);
1018}
1019
1020/* ----------------
1021   if we are root change path user/ group
1022   It can be a native function for BSD cf. FAQ.Q10
1023   path:  pathname to chown
1024   stbuf: parent directory inode
1025
1026   use fstat and fchown or lchown with linux?
1027*/
1028#define EMULATE_SUIDDIR
1029
1030static int ad_chown(const char *path, struct stat *stbuf)
1031{
1032    int ret = 0;
1033#ifdef EMULATE_SUIDDIR
1034    uid_t id;
1035
1036    if (default_uid != (uid_t)-1) {
1037        /* we are root (admin) */
1038        id = (default_uid)?default_uid:stbuf->st_uid;
1039        ret = chown(path, id, stbuf->st_gid);
1040    }
1041#endif
1042    return ret;
1043}
1044
1045/* ----------------
1046   return access right and inode of path parent directory
1047*/
1048static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
1049{
1050    if (*mode == 0) {
1051        return -1;
1052    }
1053    if (ad_stat(path, stbuf) != 0) {
1054        *mode &= DEFMASK;
1055        return -1;
1056    }
1057    *mode &= stbuf->st_mode;
1058    return 0;
1059}
1060
1061/* ----------------
1062   return access right of path parent directory
1063*/
1064int
1065ad_mode( const char *path, int mode)
1066{
1067    struct stat     stbuf;
1068    ad_mode_st(path, &mode, &stbuf);
1069    return mode;
1070}
1071
1072/*
1073 * Use mkdir() with mode bits taken from ad_mode().
1074 */
1075int
1076ad_mkdir( const char *path, int mode)
1077{
1078    int ret;
1079    int st_invalid;
1080    struct stat stbuf;
1081
1082    LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
1083        path, mode, getcwdpath());
1084
1085    st_invalid = ad_mode_st(path, &mode, &stbuf);
1086    ret = mkdir( path, mode );
1087    if (ret || st_invalid)
1088        return ret;
1089    ad_chown(path, &stbuf);
1090
1091    return ret;
1092}
1093
1094/* ----------------- */
1095static int ad_error(struct adouble *ad, int adflags)
1096{
1097    int err = errno;
1098    if ((adflags & ADFLAGS_NOHF)) {
1099        /* FIXME double check : set header offset ?*/
1100        return 0;
1101    }
1102    if ((adflags & ADFLAGS_DF)) {
1103        ad_close( ad, ADFLAGS_DF );
1104        err = errno;
1105    }
1106    return -1 ;
1107}
1108
1109static int new_rfork(const char *path, struct adouble *ad, int adflags);
1110
1111#ifdef  HAVE_PREAD
1112#define AD_SET(a)
1113#else
1114#define AD_SET(a) a = 0
1115#endif
1116
1117/* --------------------------- */
1118static int ad_check_size(struct adouble *ad _U_, struct stat *st)
1119{
1120    if (st->st_size > 0 && st->st_size < AD_DATASZ1)
1121        return 1;
1122    return 0;
1123}
1124
1125/* --------------------------- */
1126static int ad_check_size_sfm(struct adouble *ad _U_, struct stat *st)
1127{
1128    if (st->st_size > 0 && st->st_size < AD_SFM_LEN)
1129        return 1;
1130    return 0;
1131}
1132
1133/* --------------------------- */
1134static int ad_header_upgrade(struct adouble *ad, char *name)
1135{
1136#if AD_VERSION == AD_VERSION2
1137    int ret;
1138    if ( (ret = ad_convert(ad, name)) < 0 || (ret = ad_update(ad, name) < 0)) {
1139        return ret;
1140    }
1141#endif
1142    return 0;
1143}
1144
1145/* --------------------------- */
1146static int ad_header_upgrade_none(struct adouble *ad _U_, char *name _U_)
1147{
1148    return 0;
1149}
1150
1151/* --------------------------- */
1152static struct adouble_fops ad_osx = {
1153    &ad_path_osx,
1154    &ad_mkrf_osx,
1155    &ad_rebuild_adouble_header,
1156    &ad_check_size,
1157
1158    &ad_header_read,
1159    &ad_header_upgrade,
1160};
1161
1162static struct adouble_fops ad_sfm = {
1163    &ad_path_sfm,
1164    &ad_mkrf_sfm,
1165    &ad_rebuild_sfm_header,
1166    &ad_check_size_sfm,
1167
1168    &ad_header_sfm_read,
1169    &ad_header_upgrade_none,
1170};
1171
1172static struct adouble_fops ad_adouble = {
1173    &ad_path,
1174    &ad_mkrf,
1175    &ad_rebuild_adouble_header,
1176    &ad_check_size,
1177
1178    &ad_header_read,
1179    &ad_header_upgrade,
1180};
1181
1182
1183void ad_init(struct adouble *ad, int flags, int options)
1184{
1185    ad->ad_inited = 0;
1186    ad->ad_flags = flags;
1187    if (flags == AD_VERSION2_OSX) {
1188        ad->ad_ops = &ad_osx;
1189        ad->ad_md = &ad->ad_resource_fork;
1190    }
1191    else if (flags == AD_VERSION1_SFM) {
1192        ad->ad_ops = &ad_sfm;
1193        ad->ad_md = &ad->ad_metadata_fork;
1194    }
1195    else {
1196        ad->ad_ops = &ad_adouble;
1197        ad->ad_md = &ad->ad_resource_fork;
1198    }
1199    ad->ad_options = options;
1200
1201    ad_data_fileno(ad) = -1;
1202    ad_reso_fileno(ad) = -1;
1203    ad_meta_fileno(ad) = -1;
1204    /* following can be read even if there's no
1205     * meda data.
1206     */
1207    memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1208    ad->ad_rlen = 0;
1209}
1210
1211/*!
1212 * Open data-, metadata(header)- or ressource fork
1213 *
1214 * You must call ad_init() before ad_open, usually you'll just call it like this: \n
1215 * @code
1216 *      struct adoube ad;
1217 *      ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1218 * @endcode
1219 *
1220 * @param path    Path to file or directory
1221 *
1222 * @param adflags ADFLAGS_DF: open data file/fork\n
1223 *                ADFLAGS_HF: open header (metadata) file\n
1224 *                ADFLAGS_RF: open ressource fork *** FIXME: not used ?! *** \n
1225 *                ADFLAGS_CREATE: indicate creation\n
1226 *                ADFLAGS_NOHF: it's not an error if header file couldn't be created\n
1227 *                ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags\n
1228 *                ADFLAGS_NOADOUBLE: dont create adouble files if not necessary\n
1229 *                ADFLAGS_RDONLY: open read only\n
1230 *                ADFLAGS_OPENFORKS: check for open forks from other processes\n
1231 *                ADFLAGS_MD: alias for ADFLAGS_HF\n
1232 *                ADFLAGS_V1COMPAT: obsolete
1233 *
1234 * @param oflags  flags passed through to open syscall: \n
1235 *                O_RDONLY: *** FIXME *** \n
1236 *                O_RDWR: *** FIXME *** \n
1237 *                O_CREAT: create fork\n
1238 *                O_EXCL: fail if exists with O_CREAT
1239 *
1240 * @param mode    passed to open with O_CREAT
1241 *
1242 * @param ad      pointer to struct adouble
1243 *
1244 * @returns 0 on success
1245 *
1246 * @note It's not possible to open the header file O_RDONLY -- the read
1247 *       will fail and return an error. this refcounts things now.\n
1248 *       metadata(ressource)-fork only gets created with O_CREAT.
1249 */
1250int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble  *ad)
1251{
1252    struct stat         st_dir;
1253    struct stat         st_meta;
1254    struct stat         *pst = NULL;
1255    char        *ad_p;
1256    int         hoflags, admode;
1257    int                 st_invalid = -1;
1258    int                 open_df = 0;
1259
1260    if (ad->ad_inited != AD_INITED) {
1261        ad->ad_inited = AD_INITED;
1262        ad->ad_refcount = 1;
1263        ad->ad_open_forks = 0;
1264        ad->ad_adflags = adflags;
1265        ad->ad_resource_fork.adf_refcount = 0;
1266        ad->ad_data_fork.adf_refcount = 0;
1267        ad->ad_data_fork.adf_syml=0;
1268    }
1269    else {
1270        ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1271        /* XXX not true if we have a meta data fork ? */
1272        if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount))
1273            ad->ad_open_forks |= ATTRBIT_ROPEN;
1274    }
1275
1276    if ((adflags & ADFLAGS_DF)) {
1277        if (ad_data_fileno(ad) == -1) {
1278            hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1279            admode = mode;
1280            if ((oflags & O_CREAT)) {
1281                st_invalid = ad_mode_st(path, &admode, &st_dir);
1282                if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1283                    admode = mode;
1284                }
1285            }
1286
1287            ad->ad_data_fork.adf_fd = open(path, hoflags | ad_get_syml_opt(ad), admode);
1288
1289            if (ad->ad_data_fork.adf_fd == -1) {
1290                if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1291                    hoflags = oflags;
1292                    ad->ad_data_fork.adf_fd = open( path, hoflags | ad_get_syml_opt(ad), admode );
1293                }
1294                if (ad->ad_data_fork.adf_fd == -1 && errno == OPEN_NOFOLLOW_ERRNO) {
1295                    int lsz;
1296
1297                    ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
1298                    lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN);
1299                    if (lsz <= 0) {
1300                        free(ad->ad_data_fork.adf_syml);
1301                        return -1;
1302                    }
1303                    ad->ad_data_fork.adf_syml[lsz] = 0;
1304                    ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
1305                }
1306            }
1307
1308            if ( ad->ad_data_fork.adf_fd == -1 )
1309                return -1;
1310
1311            AD_SET(ad->ad_data_fork.adf_off);
1312            ad->ad_data_fork.adf_flags = hoflags;
1313            if (!st_invalid) {
1314                /* just created, set owner if admin (root) */
1315                ad_chown(path, &st_dir);
1316            }
1317            adf_lock_init(&ad->ad_data_fork);
1318        }
1319        else {
1320            /* the file is already open... but */
1321            if ((oflags & ( O_RDWR | O_WRONLY)) &&             /* we want write access */
1322                !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
1323            {
1324                errno = EACCES;
1325                return -1;
1326            }
1327            /* FIXME
1328             * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
1329             * already open. Should we check for it? ie
1330             * O_EXCL --> error
1331             * O_TRUNC --> truncate the fork.
1332             * idem for ressource fork.
1333             */
1334        }
1335        open_df = ADFLAGS_DF;
1336        ad->ad_data_fork.adf_refcount++;
1337    }
1338
1339    adf_lock_init(&ad->ad_data_fork);
1340    if (!(adflags & ADFLAGS_HF))
1341        return 0;
1342
1343    if(strcmp(path,".")==0)
1344    {
1345        char dir[1024];
1346        getcwd(dir,sizeof(dir));
1347        if(strstr(dir,"Private Directory Data"))
1348            return 0;
1349    }
1350    if(strstr(path,"Private Directory Data"))
1351        return 0;
1352
1353    /* ****************************************** */
1354
1355    if (ad_meta_fileno(ad) != -1) { /* the file is already open */
1356        if ((oflags & ( O_RDWR | O_WRONLY)) &&
1357            !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) {
1358            if (open_df) {
1359                /* don't call with ADFLAGS_HF because we didn't open ressource fork */
1360                ad_close( ad, open_df );
1361            }
1362            errno = EACCES;
1363            return -1;
1364        }
1365        ad_refresh(ad);
1366        /* it's not new anymore */
1367        ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT );
1368        ad->ad_md->adf_refcount++;
1369        goto sfm;
1370    }
1371
1372    memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1373    ad->ad_rlen = 0;
1374    ad_p = ad->ad_ops->ad_path( path, adflags );
1375    hoflags = oflags & ~(O_CREAT | O_EXCL);
1376    if (!(adflags & ADFLAGS_RDONLY)) {
1377        hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1378    }
1379    ad->ad_md->adf_fd = open(ad_p, hoflags | ad_get_syml_opt(ad), 0);
1380    if (ad->ad_md->adf_fd < 0 ) {
1381        if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1382            hoflags = oflags & ~(O_CREAT | O_EXCL);
1383            ad->ad_md->adf_fd = open(ad_p, hoflags | ad_get_syml_opt(ad), 0);
1384        }
1385    }
1386
1387    if ( ad->ad_md->adf_fd < 0 ) {
1388        if (errno == ENOENT && (oflags & O_CREAT) ) {
1389            /*
1390             * We're expecting to create a new adouble header file,
1391             * here.
1392             * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
1393             */
1394            LOG(log_debug, logtype_default, "ad_open(\"%s\"): {cwd: \"%s\"} creating adouble file",
1395                ad_p, getcwdpath());
1396            admode = mode;
1397            errno = 0;
1398            st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1399            if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1400                admode = mode;
1401            }
1402            admode = ad_hf_mode(admode);
1403            if ((errno == ENOENT) && (ad->ad_flags != AD_VERSION2_OSX)) {
1404                if (ad->ad_ops->ad_mkrf(ad_p) < 0) {
1405                    return ad_error(ad, adflags);
1406                }
1407                admode = mode;
1408                st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1409                if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1410                    admode = mode;
1411                }
1412                admode = ad_hf_mode(admode);
1413            }
1414            /* retry with O_CREAT */
1415            ad->ad_md->adf_fd = open( ad_p, oflags,admode );
1416            if ( ad->ad_md->adf_fd < 0 ) {
1417                return ad_error(ad, adflags);
1418            }
1419            ad->ad_md->adf_flags = oflags;
1420            /* just created, set owner if admin owner (root) */
1421            if (!st_invalid) {
1422                ad_chown(ad_p, &st_dir);
1423            }
1424        }
1425        else {
1426            return ad_error(ad, adflags);
1427        }
1428    } else {
1429        ad->ad_md->adf_flags = hoflags;
1430        if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
1431            /* for 0 length files, treat them as new. */
1432            ad->ad_md->adf_flags |= O_TRUNC;
1433        }
1434        else {
1435            /* we have valid data in st_meta stat structure, reused it
1436               in ad_header_read
1437            */
1438            pst = &st_meta;
1439        }
1440    }
1441    AD_SET(ad->ad_md->adf_off);
1442
1443    ad->ad_md->adf_refcount = 1;
1444    adf_lock_init(ad->ad_md);
1445    if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
1446        /*
1447         * This is a new adouble header file. Initialize the structure,
1448         * instead of reading it.
1449         */
1450        if (new_rfork(path, ad, adflags) < 0) {
1451            int err = errno;
1452            /* the file is already deleted, perm, whatever, so return an error*/
1453            ad_close(ad, adflags);
1454            errno = err;
1455            return -1;
1456        }
1457        ad_flush(ad);
1458    } else {
1459        /* Read the adouble header in and parse it.*/
1460        if (ad->ad_ops->ad_header_read( ad , pst) < 0
1461            || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0)
1462        {
1463            int err = errno;
1464
1465            ad_close( ad, adflags );
1466            errno = err;
1467            return -1;
1468        }
1469    }
1470
1471    /* ****************************************** */
1472    /* open the resource fork if SFM */
1473sfm:
1474    if (ad->ad_flags != AD_VERSION1_SFM) {
1475        return 0;
1476    }
1477
1478    if ((adflags & ADFLAGS_DIR)) {
1479        /* no resource fork for directories / volumes XXX it's false! */
1480        return 0;
1481    }
1482
1483    /* untrue yet but ad_close will decremente it*/
1484    ad->ad_resource_fork.adf_refcount++;
1485
1486    if (ad_reso_fileno(ad) != -1) { /* the file is already open */
1487        if ((oflags & ( O_RDWR | O_WRONLY)) &&
1488            !(ad->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1489
1490            ad_close( ad, open_df | ADFLAGS_HF);
1491            errno = EACCES;
1492            return -1;
1493        }
1494        return 0;
1495    }
1496
1497    ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF );
1498
1499    admode = mode;
1500    st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1501
1502    if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1503        admode = mode;
1504    }
1505
1506    hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1507    ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
1508
1509    if (ad->ad_resource_fork.adf_fd < 0 ) {
1510        if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1511            hoflags = oflags;
1512            ad->ad_resource_fork.adf_fd =open( ad_p, hoflags, admode );
1513        }
1514    }
1515
1516    if ( ad->ad_resource_fork.adf_fd < 0) {
1517        int err = errno;
1518
1519        ad_close( ad, adflags );
1520        errno = err;
1521        return -1;
1522    }
1523    adf_lock_init(&ad->ad_resource_fork);
1524    AD_SET(ad->ad_resource_fork.adf_off);
1525    ad->ad_resource_fork.adf_flags = hoflags;
1526    if ((oflags & O_CREAT) && !st_invalid) {
1527        /* just created, set owner if admin (root) */
1528        ad_chown(ad_p, &st_dir);
1529    }
1530    else if (!fstat(ad->ad_resource_fork.adf_fd, &st_meta)) {
1531        ad->ad_rlen = st_meta.st_size;
1532    }
1533    return 0 ;
1534}
1535
1536/*!
1537 * @brief open metadata, possibly as root
1538 *
1539 * Return only metadata but try very hard ie at first try as user, then try as root.
1540 *
1541 * @param name  name of file/dir
1542 * @param flags ADFLAGS_DIR: name is a directory \n
1543 *              ADFLAGS_CREATE: force creation of header file, but only as user, not as root\n
1544 *              ADFLAGS_OPENFORKS: test if name is open by another afpd process
1545 *
1546 * @param adp   pointer to struct adouble
1547 *
1548 * @note caller MUST pass ADFLAGS_DIR for directories. Whether ADFLAGS_CREATE really creates
1549 *       a adouble file depends on various other volume options, eg. ADVOL_CACHE
1550 */
1551int ad_metadata(const char *name, int flags, struct adouble *adp)
1552{
1553    uid_t uid;
1554    int   ret, err, dir;
1555    int   create = O_RDONLY;
1556
1557    dir = flags & ADFLAGS_DIR;
1558
1559    /* Check if we shall call ad_open with O_CREAT */
1560    if ( (adp->ad_options & ADVOL_CACHE)
1561         && ! (adp->ad_options & ADVOL_NOADOUBLE)
1562         && (flags & ADFLAGS_CREATE) ) {
1563        create = O_CREAT | O_RDWR;
1564    }
1565    if ((ret = ad_open(name, ADFLAGS_HF | dir, create, 0666, adp)) < 0 && errno == EACCES) {
1566        uid = geteuid();
1567        if (seteuid(0)) {
1568            LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
1569            errno = EACCES;
1570            return -1;
1571        }
1572        /* we are root open read only */
1573        ret = ad_open(name, ADFLAGS_HF|ADFLAGS_RDONLY| dir, O_RDONLY, 0, adp);
1574        err = errno;
1575        if ( seteuid(uid) < 0) {
1576            LOG(log_error, logtype_default, "ad_metadata: can't seteuid back");
1577            exit(EXITERR_SYS);
1578        }
1579        errno = err;
1580    }
1581
1582    if (!ret && (ADFLAGS_OPENFORKS & flags)) {
1583        /*
1584          we need to check if the file is open by another process.
1585          it's slow so we only do it if we have to:
1586          - it's requested.
1587          - we don't already have the answer!
1588        */
1589        adp->ad_open_forks |= ad_openforks(adp, adp->ad_open_forks);
1590    }
1591    return ret;
1592}
1593
1594/*
1595 * @brief openat like wrapper for ad_metadata
1596 */
1597int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
1598{
1599    int ret = 0;
1600    int cwdfd = -1;
1601
1602    if (dirfd != -1) {
1603        if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
1604            ret = -1;
1605            goto exit;
1606        }
1607    }
1608
1609    if (ad_metadata(name, flags, adp) < 0) {
1610        ret = -1;
1611        goto exit;
1612    }
1613
1614    if (dirfd != -1) {
1615        if (fchdir(cwdfd) != 0) {
1616            LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
1617            exit(EXITERR_SYS);
1618        }
1619    }
1620
1621exit:
1622    if (cwdfd != -1)
1623        close(cwdfd);
1624
1625    return ret;
1626
1627}
1628
1629/* ----------------------------------- */
1630static int new_rfork(const char *path, struct adouble *ad, int adflags)
1631{
1632    const struct entry  *eid;
1633    u_int16_t           ashort;
1634    struct stat         st;
1635
1636    ad->ad_magic = AD_MAGIC;
1637    ad->ad_version = ad->ad_flags & 0x0f0000;
1638    if (!ad->ad_version) {
1639        ad->ad_version = AD_VERSION;
1640    }
1641
1642    memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
1643    memset(ad->ad_data, 0, sizeof(ad->ad_data));
1644
1645#if AD_VERSION == AD_VERSION2
1646    if (ad->ad_flags == AD_VERSION2)
1647        eid = entry_order2;
1648    else if (ad->ad_flags == AD_VERSION2_OSX)
1649        eid = entry_order_osx;
1650    else  if (ad->ad_flags == AD_VERSION1_SFM) {
1651        ad->ad_magic = SFM_MAGIC;
1652        eid = entry_order_sfm;
1653    }
1654    else
1655#endif
1656        eid = entry_order1;
1657
1658    while (eid->id) {
1659        ad->ad_eid[eid->id].ade_off = eid->offset;
1660        ad->ad_eid[eid->id].ade_len = eid->len;
1661        eid++;
1662    }
1663
1664    /* make things invisible */
1665    if ((ad->ad_options & ADVOL_INVDOTS) && !(adflags & ADFLAGS_CREATE) &&
1666        (*path == '.') && strcmp(path, ".") && strcmp(path, ".."))
1667    {
1668        ashort = htons(ATTRBIT_INVISIBLE);
1669        ad_setattr(ad, ashort);
1670        ashort = htons(FINDERINFO_INVISIBLE);
1671        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1672    }
1673
1674    if (ostat(path, &st, ad_get_syml_opt(ad)) < 0) {
1675        return -1;
1676    }
1677
1678    /* put something sane in the date fields */
1679    ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
1680    ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
1681    ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
1682    ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
1683    return 0;
1684}
1685
1686/* to do this with mmap, we need the hfs fs to understand how to mmap
1687   header files. */
1688int ad_refresh(struct adouble *ad)
1689{
1690
1691    if (ad_meta_fileno(ad) < 0)
1692        return -1;
1693
1694    return ad->ad_ops->ad_header_read(ad, NULL);
1695}
1696
1697int ad_openat(int dirfd,  /* dir fd openat like */
1698              const char *path,
1699              int adflags,
1700              int oflags,
1701              int mode,
1702              struct adouble  *ad)
1703{
1704    int ret = 0;
1705    int cwdfd = -1;
1706
1707    if (dirfd != -1) {
1708        if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
1709            ret = -1;
1710            goto exit;
1711        }
1712    }
1713
1714    if (ad_open(path, adflags, oflags, mode, ad) < 0) {
1715        ret = -1;
1716        goto exit;
1717    }
1718
1719    if (dirfd != -1) {
1720        if (fchdir(cwdfd) != 0) {
1721            LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
1722            exit(EXITERR_SYS);
1723        }
1724    }
1725
1726exit:
1727    if (cwdfd != -1)
1728        close(cwdfd);
1729
1730    return ret;
1731}
1732