~ubuntu-branches/ubuntu/precise/netatalk/precise

« back to all changes in this revision

Viewing changes to libatalk/adouble/ad_open.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastian Rittau
  • Date: 2004-01-19 12:43:49 UTC
  • Revision ID: james.westby@ubuntu.com-20040119124349-es563jbp0hk0ae51
Tags: upstream-1.6.4
ImportĀ upstreamĀ versionĀ 1.6.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * $Id: ad_open.c,v 1.19.2.1 2003/04/10 23:34:57 didg Exp $
 
3
 *
 
4
 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
 
5
 * Copyright (c) 1990,1991 Regents of The University of Michigan.
 
6
 * All Rights Reserved.
 
7
 *
 
8
 * Permission to use, copy, modify, and distribute this software and
 
9
 * its documentation for any purpose and without fee is hereby granted,
 
10
 * provided that the above copyright notice appears in all copies and
 
11
 * that both that copyright notice and this permission notice appear
 
12
 * in supporting documentation, and that the name of The University
 
13
 * of Michigan not be used in advertising or publicity pertaining to
 
14
 * distribution of the software without specific, written prior
 
15
 * permission. This software is supplied as is without expressed or
 
16
 * implied warranties of any kind.
 
17
 *
 
18
 *      Research Systems Unix Group
 
19
 *      The University of Michigan
 
20
 *      c/o Mike Clark
 
21
 *      535 W. William Street
 
22
 *      Ann Arbor, Michigan
 
23
 *      +1-313-763-0525
 
24
 *      netatalk@itd.umich.edu
 
25
 */
 
26
 
 
27
#ifdef HAVE_CONFIG_H
 
28
#include "config.h"
 
29
#endif /* HAVE_CONFIG_H */
 
30
 
 
31
#include <string.h>
 
32
#ifdef HAVE_FCNTL_H
 
33
#include <fcntl.h>
 
34
#endif /* HAVE_FCNTL_H */
 
35
#ifdef HAVE_UNISTD_H
 
36
#include <unistd.h>
 
37
#endif /* HAVE_UNISTD_H */
 
38
#include <errno.h>
 
39
#include <atalk/logger.h>
 
40
 
 
41
#include <sys/time.h>
 
42
#include <sys/types.h>
 
43
#include <sys/stat.h>
 
44
#include <sys/param.h>
 
45
#include <sys/mman.h>
 
46
 
 
47
#include <netatalk/endian.h>
 
48
#include <atalk/adouble.h>
 
49
 
 
50
#include "ad_private.h"
 
51
 
 
52
#ifndef MAX
 
53
#define MAX(a, b)  ((a) < (b) ? (b) : (a))
 
54
#endif /* ! MAX */
 
55
 
 
56
/*
 
57
 * AppleDouble entry default offsets.
 
58
 * The layout looks like this:
 
59
 *
 
60
 * this is the v1 layout:
 
61
 *        255     200             16      32              N
 
62
 *      |  NAME |    COMMENT    | FILEI |    FINDERI    | RFORK |
 
63
 *
 
64
 * we need to change it to look like this:
 
65
 *
 
66
 * v2 layout:
 
67
 * field       length (in bytes)
 
68
 * NAME        255
 
69
 * COMMENT     200
 
70
 * FILEDATESI  16     replaces FILEI
 
71
 * FINDERI     32  
 
72
 * DID          4     new
 
73
 * AFPFILEI     4     new
 
74
 * SHORTNAME   12     8.3 new
 
75
 * RFORK        N
 
76
 * 
 
77
 * so, all we need to do is replace FILEI with FILEDATESI, move RFORK,
 
78
 * and add in the new fields.
 
79
 *
 
80
 * NOTE: the HFS module will need similar modifications to interact with
 
81
 * afpd correctly.
 
82
 */
 
83
 
 
84
#define ADEDOFF_MAGIC        (0)
 
85
#define ADEDOFF_VERSION      (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
 
86
#define ADEDOFF_FILLER       (ADEDOFF_VERSION + ADEDLEN_VERSION)
 
87
#define ADEDOFF_NENTRIES     (ADEDOFF_FILLER + ADEDLEN_FILLER)
 
88
 
 
89
/* initial lengths of some of the fields */
 
90
#define ADEDLEN_INIT     0
 
91
 
 
92
/* make sure we don't redefine ADEDOFF_FILEI */
 
93
#ifdef ADEDOFF_FILEI
 
94
#undef ADEDOFF_FILEI
 
95
#endif /* ADEDOFF_FILEI */
 
96
 
 
97
#define ADEID_NUM_V1         5
 
98
#define ADEDOFF_NAME_V1      (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
 
99
#define ADEDOFF_COMMENT_V1   (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
 
100
#define ADEDOFF_FILEI        (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
 
101
#define ADEDOFF_FINDERI_V1   (ADEDOFF_FILEI + ADEDLEN_FILEI)
 
102
#define ADEDOFF_RFORK_V1     (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
 
103
 
 
104
/* i stick things in a slightly different order than their eid order in 
 
105
 * case i ever want to separate RootInfo behaviour from the rest of the 
 
106
 * stuff. */
 
107
#define ADEID_NUM_V2         9
 
108
#define ADEDOFF_NAME_V2      (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
 
109
#define ADEDOFF_COMMENT_V2   (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
 
110
#define ADEDOFF_FILEDATESI   (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
 
111
#define ADEDOFF_FINDERI_V2   (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
 
112
#define ADEDOFF_DID          (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
 
113
#define ADEDOFF_AFPFILEI     (ADEDOFF_DID + ADEDLEN_DID)
 
114
#define ADEDOFF_SHORTNAME    (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
 
115
#define ADEDOFF_PRODOSFILEI  (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
 
116
#define ADEDOFF_RFORK_V2     (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
 
117
 
 
118
 
 
119
 
 
120
/* we keep local copies of a bunch of stuff so that we can initialize things 
 
121
 * correctly. */
 
122
 
 
123
/* Bits in the finderinfo data. 
 
124
 * see etc/afpd/{directory.c,file.c} for the finderinfo structure
 
125
 * layout. */
 
126
#define FINDERINFO_CUSTOMICON 0x4
 
127
#define FINDERINFO_CLOSEDVIEW 0x100
 
128
 
 
129
/* offsets in finderinfo */
 
130
#define FINDERINFO_FRTYPEOFF   0
 
131
#define FINDERINFO_FRCREATOFF  4
 
132
#define FINDERINFO_FRFLAGOFF   8
 
133
#define FINDERINFO_FRVIEWOFF  14
 
134
 
 
135
/* invisible bit for dot files */
 
136
#define ATTRBIT_INVISIBLE     (1 << 0)
 
137
#define FINDERINFO_INVISIBLE  (1 << 14)
 
138
 
 
139
/* this is to prevent changing timezones from causing problems with
 
140
   localtime volumes. the screw-up is 30 years. we use a delta of 5
 
141
   years.  */
 
142
#define TIMEWARP_DELTA 157680000
 
143
 
 
144
 
 
145
struct entry {
 
146
  u_int32_t id, offset, len;
 
147
};
 
148
 
 
149
#if AD_VERSION == AD_VERSION1 
 
150
static const struct entry entry_order[] = {
 
151
  {ADEID_NAME, ADEDOFF_NAME_V1, ADEDLEN_INIT},
 
152
  {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT},
 
153
  {ADEID_FILEI, ADEDOFF_FILEI, ADEDLEN_FILEI},
 
154
  {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI},
 
155
  {ADEID_RFORK, ADEDOFF_RFORK_V1, ADEDLEN_INIT},
 
156
  {0, 0, 0}
 
157
};
 
158
#else /* AD_VERSION == AD_VERSION2 */
 
159
static const struct entry entry_order[] = {
 
160
  {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
 
161
  {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
 
162
  {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
 
163
  {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
 
164
  {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
 
165
  {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
 
166
  {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
 
167
  {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
 
168
  {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
 
169
  {0, 0, 0}
 
170
};
 
171
#endif /* AD_VERSION == AD_VERSION2 */
 
172
 
 
173
#if AD_VERSION == AD_VERSION2
 
174
 
 
175
 
 
176
static __inline__ int ad_v1tov2(struct adouble *ad, const char *path)
 
177
{
 
178
  struct stat st;
 
179
  struct timeval tv;
 
180
  u_int16_t attr;
 
181
  char *buf;
 
182
  int fd, off;
 
183
  
 
184
  /* check to see if we should convert this header. */
 
185
  if (!path || (ad->ad_version != AD_VERSION1))
 
186
    return 0;
 
187
 
 
188
  /* convert from v1 to v2. what does this mean?
 
189
   *  1) change FILEI into FILEDATESI
 
190
   *  2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
 
191
   *  3) move FILEI attributes into AFPFILEI
 
192
   *  4) initialize ACCESS field of FILEDATESI.
 
193
   *
 
194
   *  so, we need 4*12 (entry ids) + 12 (shortname) + 4 (afpfilei) +
 
195
   *  4 (did) + 8 (prodosi) = 76 more bytes.  */
 
196
  
 
197
#define SHIFTDATA (AD_DATASZ2 - AD_DATASZ1)
 
198
 
 
199
  /* bail if we can't get a lock */
 
200
  if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0) 
 
201
    goto bail_err;
 
202
  
 
203
  if ((fd = open(path, O_RDWR)) < 0) 
 
204
    goto bail_lock;
 
205
  
 
206
  if (gettimeofday(&tv, NULL) < 0) 
 
207
    goto bail_lock;
 
208
  
 
209
  if (fstat(fd, &st) ||
 
210
      ftruncate(fd, st.st_size + SHIFTDATA) < 0) {
 
211
    goto bail_open;
 
212
  }
 
213
  
 
214
  /* last place for failure. */
 
215
  if ((void *) (buf = (char *) 
 
216
                mmap(NULL, st.st_size + SHIFTDATA,
 
217
                     PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == 
 
218
          MAP_FAILED) {
 
219
    goto bail_truncate;
 
220
  }
 
221
  
 
222
  off = ad->ad_eid[ADEID_RFORK].ade_off;
 
223
 
 
224
#ifdef USE_MMAPPED_HEADERS
 
225
  /* okay, unmap our old ad header and point it to our local copy */
 
226
  munmap(ad->ad_data, off);
 
227
  ad->ad_data = buf;
 
228
#endif /* USER_MMAPPED_HEADERS */
 
229
 
 
230
  /* move the RFORK. this assumes that the RFORK is at the end */
 
231
  memmove(buf + off + SHIFTDATA, buf + off, 
 
232
          ad->ad_eid[ADEID_RFORK].ade_len);
 
233
  
 
234
  /* now, fix up our copy of the header */
 
235
  memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
 
236
  
 
237
  /* replace FILEI with FILEDATESI */
 
238
  ad_getattr(ad, &attr);
 
239
  ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
 
240
  ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
 
241
  ad->ad_eid[ADEID_FILEI].ade_off = 0;
 
242
  ad->ad_eid[ADEID_FILEI].ade_len = 0;
 
243
  
 
244
  /* add in the new entries */
 
245
  ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
 
246
  ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
 
247
  ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
 
248
  ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
 
249
  ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
 
250
  ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
 
251
  ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
 
252
  ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
 
253
  
 
254
  /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
 
255
  ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
 
256
  ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
 
257
  ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
 
258
  ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
 
259
  
 
260
  /* switch to v2 */
 
261
  ad->ad_version = AD_VERSION2;
 
262
  
 
263
  /* move our data buffer to make space for the new entries. */
 
264
  memmove(buf + ADEDOFF_NAME_V2, buf + ADEDOFF_NAME_V1,
 
265
          ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
 
266
  
 
267
  /* now, fill in the space with appropriate stuff. we're
 
268
     operating as a v2 file now. */
 
269
  ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, tv.tv_sec);
 
270
  memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
 
271
  memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
 
272
  ad_setattr(ad, attr);
 
273
  memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
 
274
  memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
 
275
  
 
276
  /* rebuild the header and cleanup */
 
277
  ad_rebuild_header(ad);
 
278
  munmap(buf, st.st_size + SHIFTDATA);
 
279
  close(fd);
 
280
  ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
 
281
 
 
282
#ifdef USE_MMAPPED_HEADERS
 
283
  /* now remap our header */
 
284
  ad->ad_data = mmap(NULL, ADEDOFF_RFORK_V2, PROT_READ | PROT_WRITE,
 
285
                     (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? MAP_SHARED :
 
286
                     MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
 
287
  if (ad->ad_data == MAP_FAILED)
 
288
    goto bail_err;
 
289
#endif /* USE_MMAPPED_HEADERS */
 
290
 
 
291
  return 0;
 
292
  
 
293
bail_truncate:
 
294
  ftruncate(fd, st.st_size);
 
295
bail_open:
 
296
  close(fd);
 
297
bail_lock:
 
298
  ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
 
299
bail_err:
 
300
  return -1;
 
301
}
 
302
#endif /* AD_VERSION == AD_VERSION2 */
 
303
 
 
304
 
 
305
/* read in the entries */
 
306
static __inline__ void parse_entries(struct adouble *ad, char *buf,
 
307
                                    u_int16_t nentries)
 
308
{
 
309
    u_int32_t           eid, len, off;
 
310
 
 
311
    /* now, read in the entry bits */
 
312
    for (; nentries > 0; nentries-- ) {
 
313
        memcpy(&eid, buf, sizeof( eid ));
 
314
        eid = ntohl( eid );
 
315
        buf += sizeof( eid );
 
316
        memcpy(&off, buf, sizeof( off ));
 
317
        off = ntohl( off );
 
318
        buf += sizeof( off );
 
319
        memcpy(&len, buf, sizeof( len ));
 
320
        len = ntohl( len );
 
321
        buf += sizeof( len );
 
322
 
 
323
        if ( 0 < eid && eid < ADEID_MAX ) {
 
324
            ad->ad_eid[ eid ].ade_off = off;
 
325
            ad->ad_eid[ eid ].ade_len = len;
 
326
        } else {
 
327
            LOG(log_debug, logtype_default, "ad_refresh: nentries %hd  eid %d\n",
 
328
                    nentries, eid );
 
329
        }
 
330
    }
 
331
}
 
332
 
 
333
 
 
334
/* this reads enough of the header so that we can figure out all of
 
335
 * the entry lengths and offsets. once that's done, we just read/mmap
 
336
 * the rest of the header in.
 
337
 *
 
338
 * NOTE: we're assuming that the resource fork is kept at the end of
 
339
 *       the file. also, mmapping won't work for the hfs fs until it
 
340
 *       understands how to mmap header files. */
 
341
static __inline__ int ad_header_read(struct adouble *ad)
 
342
{
 
343
#ifdef USE_MMAPPED_HEADERS
 
344
    char                buf[AD_ENTRY_LEN*ADEID_MAX];
 
345
#else /* USE_MMAPPED_HEADERS */
 
346
    char                *buf = ad->ad_data;
 
347
#endif /* USE_MMAPPED_HEADERS */
 
348
    u_int16_t           nentries;
 
349
    int                 len;
 
350
    static int          warning = 0;
 
351
 
 
352
    /* read the header */
 
353
    if ( ad->ad_hf.adf_off != 0 ) {
 
354
      if ( lseek( ad->ad_hf.adf_fd, 0L, SEEK_SET ) < 0L ) {
 
355
                return( -1 );
 
356
      }
 
357
      ad->ad_hf.adf_off = 0;
 
358
    }
 
359
 
 
360
    if (read( ad->ad_hf.adf_fd, buf, AD_HEADER_LEN) != AD_HEADER_LEN) {
 
361
        if ( errno == 0 ) {
 
362
            errno = EIO;
 
363
        }
 
364
        return( -1 );
 
365
    }
 
366
    ad->ad_hf.adf_off = AD_HEADER_LEN;
 
367
 
 
368
    memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
 
369
    memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, 
 
370
           sizeof( ad->ad_version ));
 
371
 
 
372
    /* tag broken v1 headers. just assume they're all right. 
 
373
     * we detect two cases: null magic/version
 
374
     *                      byte swapped magic/version
 
375
     * XXX: in the future, you'll need the v1compat flag. 
 
376
     * (ad->ad_flags & ADFLAGS_V1COMPAT) */
 
377
    if (!ad->ad_magic && !ad->ad_version) {
 
378
      if (!warning) {
 
379
        LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
 
380
        warning++;
 
381
      }
 
382
      ad->ad_magic = AD_MAGIC;
 
383
      ad->ad_version = AD_VERSION1;
 
384
 
 
385
    } else if ((ad->ad_magic == AD_MAGIC) && 
 
386
               (ad->ad_version == AD_VERSION1)) {
 
387
      if (!warning) {
 
388
        LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
 
389
        warning++;
 
390
      }
 
391
 
 
392
    } else {
 
393
      ad->ad_magic = ntohl( ad->ad_magic );
 
394
      ad->ad_version = ntohl( ad->ad_version );
 
395
    }
 
396
 
 
397
    if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
 
398
#if AD_VERSION == AD_VERSION2
 
399
                                       && (ad->ad_version != AD_VERSION2)
 
400
#endif /* AD_VERSION == AD_VERSION2 */
 
401
                                       )) {
 
402
      errno = EIO;
 
403
      LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
 
404
      return -1;
 
405
    }
 
406
 
 
407
    memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
 
408
    memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
 
409
    nentries = ntohs( nentries );
 
410
 
 
411
    /* read in all the entry headers. if we have more than the 
 
412
     * maximum, just hope that the rfork is specified early on. */
 
413
    len = nentries*AD_ENTRY_LEN;
 
414
#ifdef USE_MMAPPED_HEADERS
 
415
    if (len > sizeof(buf))
 
416
      len = sizeof(buf);
 
417
#else /* USE_MMAPPED_HEADERS */
 
418
    if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
 
419
      len = sizeof(ad->ad_data) - AD_HEADER_LEN;
 
420
    buf += AD_HEADER_LEN;
 
421
#endif /* USE_MMAPPED_HEADERS */
 
422
    if (read(ad->ad_hf.adf_fd, buf, len) != len) {
 
423
        if (errno == 0)
 
424
            errno = EIO;
 
425
        LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
 
426
        return -1;
 
427
    }
 
428
    ad->ad_hf.adf_off += len;
 
429
 
 
430
    /* figure out all of the entry offsets and lengths. if we aren't
 
431
     * able to read a resource fork entry, bail. */
 
432
    parse_entries(ad, buf, nentries);
 
433
    if (!ad_getentryoff(ad, ADEID_RFORK)
 
434
#ifndef USE_MMAPPED_HEADERS
 
435
        || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
 
436
#endif /* ! USE_MMAPPED_HEADERS */
 
437
        ) {
 
438
      LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); 
 
439
      return -1;
 
440
    }
 
441
 
 
442
    /* read/mmap up to the beginning of the resource fork. */
 
443
#ifdef USE_MMAPPED_HEADERS
 
444
    ad->ad_data = mmap(NULL, ad_getentryoff(ad, ADEID_RFORK),
 
445
                       PROT_READ | PROT_WRITE,
 
446
                       (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? MAP_SHARED :
 
447
                       MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
 
448
    if (ad->ad_data == MAP_FAILED) 
 
449
      return -1;
 
450
#else /* USE_MMAPPED_HEADERS */
 
451
    buf += len;
 
452
    len = ad_getentryoff(ad, ADEID_RFORK) - ad->ad_hf.adf_off;
 
453
    if (read(ad->ad_hf.adf_fd, buf, len) != len) {
 
454
        if (errno == 0)
 
455
            errno = EIO;
 
456
        LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
 
457
        return -1;
 
458
    }
 
459
#endif /* USE_MMAPPED_HEADERS */
 
460
    
 
461
    /* fix up broken dates */
 
462
    if (ad->ad_version == AD_VERSION1) {
 
463
      struct stat st;
 
464
      u_int32_t aint;
 
465
      
 
466
      if (fstat(ad->ad_hf.adf_fd, &st) < 0) {
 
467
        return 1; /* fail silently */
 
468
      }
 
469
      
 
470
      /* check to see if the ad date is wrong. just see if we have
 
471
      * a modification date in the future. */
 
472
      if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
 
473
          (aint > TIMEWARP_DELTA + st.st_mtime)) {
 
474
        ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
 
475
        ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
 
476
        ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
 
477
        ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
 
478
        ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
 
479
      }
 
480
    }
 
481
 
 
482
    return 0;
 
483
}
 
484
 
 
485
 
 
486
/*
 
487
 * Put the .AppleDouble where it needs to be:
 
488
 *
 
489
 *          /   a/.AppleDouble/b
 
490
 *      a/b
 
491
 *          \   b/.AppleDouble/.Parent
 
492
 */
 
493
char *
 
494
ad_path( path, adflags )
 
495
    char        *path;
 
496
    int         adflags;
 
497
{
 
498
    static char pathbuf[ MAXPATHLEN + 1];
 
499
    char        c, *slash, buf[MAXPATHLEN + 1];
 
500
 
 
501
    strncpy(buf, path, MAXPATHLEN);
 
502
    if ( adflags & ADFLAGS_DIR ) {
 
503
        strncpy( pathbuf, buf, MAXPATHLEN );
 
504
        if ( *buf != '\0' ) {
 
505
            strcat( pathbuf, "/" );
 
506
        }
 
507
        slash = ".Parent";
 
508
    } else {
 
509
        if (( slash = strrchr( buf, '/' )) != NULL ) {
 
510
            c = *++slash;
 
511
            *slash = '\0';
 
512
            strncpy( pathbuf, buf, MAXPATHLEN);
 
513
            *slash = c;
 
514
        } else {
 
515
            pathbuf[ 0 ] = '\0';
 
516
            slash = buf;
 
517
        }
 
518
    }
 
519
    strncat( pathbuf, ".AppleDouble/", MAXPATHLEN - strlen(pathbuf));
 
520
    strncat( pathbuf, slash, MAXPATHLEN - strlen(pathbuf));
 
521
 
 
522
    return( pathbuf );
 
523
}
 
524
 
 
525
/*
 
526
 * Support inherited protection modes for AppleDouble files.  The supplied
 
527
 * mode is ANDed with the parent directory's mask value in lieu of "umask",
 
528
 * and that value is returned.
 
529
 */
 
530
 
 
531
#define DEFMASK 07700   /* be conservative */
 
532
 
 
533
char 
 
534
*ad_dir(path)
 
535
    char                *path;
 
536
{
 
537
    static char         modebuf[ MAXPATHLEN + 1];
 
538
    char                *slash;
 
539
 
 
540
    if ( strlen( path ) >= MAXPATHLEN ) {
 
541
        return NULL;  /* can't do it */
 
542
    }
 
543
 
 
544
    /*
 
545
     * For a path with directories in it, remove the final component
 
546
     * (path or subdirectory name) to get the name we want to stat.
 
547
     * For a path which is just a filename, use "." instead.
 
548
     */
 
549
    strcpy( modebuf, path );
 
550
    if (( slash = strrchr( modebuf, '/' )) != NULL ) {
 
551
        *slash = '\0';          /* remove pathname component */
 
552
    } else {
 
553
        modebuf[0] = '.';       /* use current directory */
 
554
        modebuf[1] = '\0';
 
555
    }
 
556
    return modebuf;
 
557
}
 
558
 
 
559
int
 
560
ad_mode( path, mode )
 
561
    char                *path;
 
562
    int                 mode;
 
563
{
 
564
    struct stat         stbuf;
 
565
    char                *p;
 
566
    
 
567
    if ( mode == 0 ) {
 
568
        return( mode );         /* save on syscalls */
 
569
    }
 
570
    p = ad_dir(path);
 
571
    if (!p) {
 
572
        return( mode & DEFMASK );  /* can't do it */
 
573
    }
 
574
 
 
575
    if ( stat( p, &stbuf ) != 0 ) {
 
576
        return( mode & DEFMASK );       /* bail out... can't stat dir? */
 
577
    }
 
578
 
 
579
    return( mode & stbuf.st_mode );
 
580
}
 
581
 
 
582
/*
 
583
 * Use mkdir() with mode bits taken from ad_mode().
 
584
 */
 
585
int
 
586
ad_mkdir( path, mode )
 
587
    char                *path;
 
588
    int                 mode;
 
589
{
 
590
#ifdef DEBUG
 
591
    LOG(log_info, logtype_default, "ad_mkdir: Creating directory with mode %d", mode);
 
592
#endif /* DEBUG */
 
593
    return mkdir( path, ad_mode( path, mode ) );
 
594
}
 
595
 
 
596
 
 
597
/*
 
598
 * It's not possible to open the header file O_RDONLY -- the read
 
599
 * will fail and return an error. this refcounts things now. 
 
600
 */
 
601
int ad_open( path, adflags, oflags, mode, ad )
 
602
    char                *path;
 
603
    int                 adflags, oflags, mode;
 
604
    struct adouble      *ad;
 
605
{
 
606
    const struct entry  *eid;
 
607
    struct stat         st;
 
608
    char                *slash, *ad_p;
 
609
    int                 hoflags, admode;
 
610
    u_int16_t           ashort;
 
611
 
 
612
    if (ad->ad_inited != AD_INITED) {
 
613
        ad_dfileno(ad) = -1;
 
614
        ad_hfileno(ad) = -1;
 
615
        adf_lock_init(&ad->ad_df);
 
616
        adf_lock_init(&ad->ad_hf);
 
617
#ifdef USE_MMAPPED_HEADERS
 
618
        ad->ad_data = MAP_FAILED;
 
619
#endif /* USE_MMAPPED_HEADERS */
 
620
        ad->ad_inited = AD_INITED;
 
621
        ad->ad_refcount = 1;
 
622
    }
 
623
 
 
624
    if (adflags & ADFLAGS_DF) { 
 
625
        if (ad_dfileno(ad) == -1) {
 
626
          hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
 
627
          admode = ad_mode( path, mode ); 
 
628
          if (( ad->ad_df.adf_fd = open( path, hoflags, admode )) < 0 ) {
 
629
             if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
 
630
                hoflags = oflags;
 
631
                ad->ad_df.adf_fd =open( path, hoflags, admode );
 
632
             }
 
633
          }
 
634
          if ( ad->ad_df.adf_fd < 0)
 
635
                return -1;      
 
636
          ad->ad_df.adf_off = 0;
 
637
          ad->ad_df.adf_flags = hoflags;
 
638
        } 
 
639
        else {
 
640
            /* the file is already open... but */
 
641
            if ((oflags & ( O_RDWR | O_WRONLY)) &&             /* we want write access */
 
642
                !(ad->ad_df.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
 
643
            {
 
644
                 errno = EACCES;
 
645
                 return -1;
 
646
            }
 
647
        }
 
648
        ad->ad_df.adf_refcount++;
 
649
    }
 
650
 
 
651
    if (adflags & ADFLAGS_HF) {
 
652
        if (ad_hfileno(ad) == -1) {
 
653
          ad_p = ad_path( path, adflags );
 
654
 
 
655
          hoflags = oflags & ~O_CREAT;
 
656
          hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
 
657
          if (( ad->ad_hf.adf_fd = open( ad_p, hoflags, 0 )) < 0 ) {
 
658
            if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
 
659
                hoflags = oflags & ~O_CREAT;
 
660
                ad->ad_hf.adf_fd = open( ad_p, hoflags, 0 );
 
661
            }    
 
662
          }
 
663
          if ( ad->ad_hf.adf_fd < 0 ) {
 
664
            if (errno == ENOENT && (oflags & O_CREAT) ) {
 
665
              /*
 
666
               * We're expecting to create a new adouble header file,
 
667
               * here.
 
668
               * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
 
669
               */
 
670
              admode = ad_hf_mode(ad_mode( ad_p, mode )); 
 
671
              errno = 0;
 
672
              if (( ad->ad_hf.adf_fd = open( ad_p, oflags,
 
673
                                             admode )) < 0 ) {
 
674
                /*
 
675
                 * Probably .AppleDouble doesn't exist, try to
 
676
                 * mkdir it.
 
677
                 */
 
678
                if ((errno == ENOENT) && 
 
679
                    ((adflags & ADFLAGS_NOADOUBLE) == 0)) {
 
680
                  if (( slash = strrchr( ad_p, '/' )) == NULL ) {
 
681
                    ad_close( ad, adflags );
 
682
                    return( -1 );
 
683
                  }
 
684
                  *slash = '\0';
 
685
                  errno = 0;
 
686
                  if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
 
687
                    ad_close( ad, adflags );
 
688
                    return( -1 );
 
689
                  }
 
690
                  *slash = '/';
 
691
                  if (( ad->ad_hf.adf_fd = 
 
692
                        open( ad_p, oflags, ad_mode( ad_p, mode) )) < 0 ) {
 
693
                    ad_close( ad, adflags );
 
694
                    return( -1 );
 
695
                  }
 
696
                } else {
 
697
                  ad_close( ad, adflags );
 
698
                  return( -1 );
 
699
                }
 
700
              }
 
701
              ad->ad_hf.adf_flags = oflags;
 
702
            } else {
 
703
              ad_close( ad, adflags );
 
704
              return( -1 );
 
705
            }
 
706
          } else if ((fstat(ad->ad_hf.adf_fd, &st) == 0) && 
 
707
                     (st.st_size == 0)) {
 
708
            /* for 0 length files, treat them as new. */
 
709
            ad->ad_hf.adf_flags = (oflags & ~O_RDONLY) | O_RDWR;
 
710
          } else {
 
711
            ad->ad_hf.adf_flags = hoflags;
 
712
          }
 
713
          ad->ad_hf.adf_off = 0;
 
714
          
 
715
          /*
 
716
           * This is a new adouble header file. Initialize the structure,
 
717
           * instead of reading it.
 
718
           */
 
719
          memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
 
720
          if ( ad->ad_hf.adf_flags & ( O_TRUNC | O_CREAT )) {
 
721
            struct timeval tv;
 
722
 
 
723
            ad->ad_magic = AD_MAGIC;
 
724
            ad->ad_version = AD_VERSION;
 
725
            memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
 
726
 
 
727
#ifdef USE_MMAPPED_HEADERS
 
728
            /* truncate the header file and mmap it. */
 
729
            ftruncate(ad->ad_hf.adf_fd, AD_DATASZ);
 
730
            ad->ad_data = mmap(NULL, AD_DATASZ, PROT_READ | PROT_WRITE,
 
731
                               MAP_SHARED, ad->ad_hf.adf_fd, 0);
 
732
            if (ad->ad_data == MAP_FAILED) {
 
733
              ad_close(ad, adflags);
 
734
              return -1;
 
735
            }       
 
736
#else /* USE_MMAPPED_HEADERS */
 
737
            memset(ad->ad_data, 0, sizeof(ad->ad_data));
 
738
#endif /* USE_MMAPPED_HEADERS */
 
739
 
 
740
            eid = entry_order;
 
741
            while (eid->id) {
 
742
              ad->ad_eid[eid->id].ade_off = eid->offset;
 
743
              ad->ad_eid[eid->id].ade_len = eid->len;
 
744
              eid++;
 
745
            }
 
746
            
 
747
            /* put something sane in the directory finderinfo */
 
748
            if (adflags & ADFLAGS_DIR) {
 
749
              /* set default view */
 
750
              ashort = htons(FINDERINFO_CLOSEDVIEW);
 
751
              memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, 
 
752
                     &ashort, sizeof(ashort));
 
753
            } else {
 
754
              /* set default creator/type fields */
 
755
              memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,
 
756
                     "TEXT", 4);
 
757
              memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,
 
758
                     "UNIX", 4);
 
759
            }
 
760
 
 
761
            /* make things invisible */
 
762
            if ((*path == '.') && strcmp(path, ".") && strcmp(path, "..")) {
 
763
              ashort = htons(ATTRBIT_INVISIBLE);
 
764
              ad_setattr(ad, ashort);
 
765
              ashort = htons(FINDERINFO_INVISIBLE);
 
766
              memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,
 
767
                     &ashort, sizeof(ashort));
 
768
            }
 
769
 
 
770
            if (gettimeofday(&tv, NULL) < 0) {
 
771
              ad_close(ad, adflags);
 
772
              return -1;
 
773
            }
 
774
            
 
775
            /* put something sane in the date fields */
 
776
            ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, tv.tv_sec);
 
777
            ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, tv.tv_sec);
 
778
            ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, tv.tv_sec);
 
779
            ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
 
780
 
 
781
          } else {
 
782
            /*
 
783
             * Read the adouble header in and parse it.
 
784
             */
 
785
            if ((ad_header_read( ad ) < 0)
 
786
#if AD_VERSION == AD_VERSION2
 
787
                || (ad_v1tov2(ad, ad_p) < 0)
 
788
#endif /* AD_VERSION == AD_VERSION2 */
 
789
                ) {
 
790
              ad_close( ad, adflags );
 
791
              return( -1 );
 
792
            }
 
793
          }
 
794
        }
 
795
        else { /* already open */
 
796
            if ((oflags & ( O_RDWR | O_WRONLY)) &&             
 
797
                !(ad->ad_hf.adf_flags & ( O_RDWR | O_WRONLY))) {
 
798
                if (adflags & ADFLAGS_DF) {
 
799
                    /* don't call with ADFLAGS_HF because we didn't open ressource fork */
 
800
                    ad_close( ad, ADFLAGS_DF );
 
801
                 }
 
802
                 errno = EACCES;
 
803
                 return -1;
 
804
            }
 
805
        }
 
806
        ad->ad_hf.adf_refcount++;
 
807
    }
 
808
        
 
809
    return( 0 );
 
810
}
 
811
 
 
812
/* to do this with mmap, we need the hfs fs to understand how to mmap
 
813
   header files. */
 
814
int ad_refresh(struct adouble *ad)
 
815
{
 
816
#ifdef USE_MMAPPED_HEADERS
 
817
  off_t off;
 
818
#endif /* USE_MMAPPED_HEADERS */
 
819
 
 
820
  if (ad->ad_hf.adf_fd < -1)
 
821
    return -1;
 
822
 
 
823
#ifdef USE_MMAPPED_HEADERS
 
824
  if (ad->ad_data == MAP_FAILED)
 
825
    return -1;
 
826
  
 
827
  /* re-read the header */
 
828
  off = ad_getentryoff(ad, ADEID_RFORK);
 
829
  memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof(nentries));
 
830
  nentries = ntohs(nentries);
 
831
  parse_entries(ad, ad->ad_data + AD_HEADER_LEN, nentries);
 
832
 
 
833
  /* check to see if something screwy happened */
 
834
  if (!ad_getentryoff(ad, ADEID_RFORK))
 
835
    return -1;
 
836
 
 
837
  /* if there's a length discrepancy, remap the header. this shouldn't
 
838
   * really ever happen. */
 
839
  if (off != ad_getentryoff(ad, ADEID_RFORK)) {
 
840
    char *buf = ad->ad_data;
 
841
    buf = ad->ad_data;
 
842
    ad->ad_data = mmap(NULL, ad_getentryoff(ad, ADEID_RFORK), 
 
843
                       PROT_READ | PROT_WRITE,
 
844
                       (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? 
 
845
                       MAP_SHARED : MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
 
846
    if (ad->ad_data == MAP_FAILED) {
 
847
      ad->ad_data = buf;
 
848
      return -1;
 
849
    }
 
850
    munmap(buf, off);
 
851
  }
 
852
  return 0;
 
853
 
 
854
#else /* USE_MMAPPED_HEADERS */
 
855
  return ad_header_read(ad);
 
856
#endif /* USE_MMAPPED_HEADERS */
 
857
}