PageRenderTime 43ms CodeModel.GetById 2ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

/filesystems/unixfs/ancientfs/ancientfs_ar.c

http://macfuse.googlecode.com/
C | 540 lines | 394 code | 91 blank | 55 comment | 69 complexity | d95bec4866ff86a7e85435ee2a86a918 MD5 | raw file
  1/*
  2 * Ancient UNIX File Systems for MacFUSE
  3 * Amit Singh
  4 * http://osxbook.com
  5 */
  6
  7#include "ancientfs_ar.h"
  8#include "unixfs_common.h"
  9
 10#include <errno.h>
 11#include <fcntl.h>
 12#include <stdio.h>
 13#include <stdlib.h>
 14#include <string.h>
 15#include <unistd.h>
 16#include <sys/ioctl.h>
 17#include <sys/stat.h>
 18
 19DECL_UNIXFS("UNIX ar", ar);
 20
 21/*
 22 * The header reading function is adapted from the BSD version.
 23 */
 24
 25/*-
 26 * Copyright (c) 1990, 1993, 1994
 27 *      The Regents of the University of California.  All rights reserved.
 28 *
 29 * This code is derived from software contributed to Berkeley by
 30 * Hugh Smith at The University of Guelph.
 31 *
 32 * Redistribution and use in source and binary forms, with or without
 33 * modification, are permitted provided that the following conditions
 34 * are met:
 35 * 1. Redistributions of source code must retain the above copyright
 36 *    notice, this list of conditions and the following disclaimer.
 37 * 2. Redistributions in binary form must reproduce the above copyright
 38 *    notice, this list of conditions and the following disclaimer in the
 39 *    documentation and/or other materials provided with the distribution.
 40 * 3. All advertising materials mentioning features or use of this software
 41 *    must display the following acknowledgement:
 42 *      This product includes software developed by the University of
 43 *      California, Berkeley and its contributors.
 44 * 4. Neither the name of the University nor the names of its contributors
 45 *    may be used to endorse or promote products derived from this software
 46 *    without specific prior written permission.
 47 *
 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 51 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 58 * SUCH DAMAGE.
 59 */
 60
 61/* Convert ar header field to an integer. */
 62#define AR_ATOI(from, to, len, base) { \
 63        memmove(buf, from, len); \
 64        buf[len] = '\0'; \
 65        to = strtol(buf, (char **)NULL, base); \
 66}
 67
 68struct chdr {
 69    off_t   size;  /* size of the object in bytes */
 70    off_t   addr;  /* where the file begins */
 71    time_t  date;  /* date */
 72    int     lname; /* size of the long name in bytes */
 73    gid_t   gid;   /* group */
 74    uid_t   uid;   /* owner */
 75    u_short mode;  /* permissions */
 76    char    name[UNIXFS_MAXNAMLEN + 1]; /* name */
 77};
 78
 79static int ancientfs_ar_readheader(int fd, struct chdr* chdr);
 80
 81static int
 82ancientfs_ar_readheader(int fd, struct chdr* chdr)
 83{
 84    int len, nr;
 85    char *p, buf[20];
 86    char hb[sizeof(struct ar_hdr) + 1];
 87    struct ar_hdr* hdr;
 88
 89    nr = read(fd, hb, sizeof(struct ar_hdr));
 90    if (nr != sizeof(struct ar_hdr)) {
 91        if (!nr)
 92            return 1;
 93        if (nr < 0)
 94            return -1;
 95    }
 96
 97    hdr = (struct ar_hdr*)hb;
 98    if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(ARFMAG) - 1))
 99        return -2;
100
101    /* Convert the header into the internal format. */
102#define DECIMAL 10
103#define OCTAL    8
104
105    AR_ATOI(hdr->ar_date, chdr->date, sizeof(hdr->ar_date), DECIMAL);
106    AR_ATOI(hdr->ar_uid, chdr->uid, sizeof(hdr->ar_uid), DECIMAL);
107    AR_ATOI(hdr->ar_gid, chdr->gid, sizeof(hdr->ar_gid), DECIMAL);
108    AR_ATOI(hdr->ar_mode, chdr->mode, sizeof(hdr->ar_mode), OCTAL);
109    AR_ATOI(hdr->ar_size, chdr->size, sizeof(hdr->ar_size), DECIMAL);
110
111    /* Leading spaces should never happen. */
112    if (hdr->ar_name[0] == ' ')
113        return -2;
114
115    /*
116     * Long name support.  Set the "real" size of the file, and the
117     * long name flag/size.
118     */
119    if (!bcmp(hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1)) {
120        chdr->lname = len = atoi(hdr->ar_name + sizeof(AR_EFMT1) - 1);
121        if (len <= 0 || len > UNIXFS_MAXNAMLEN)
122                return -1;
123        nr = read(fd, chdr->name, len);
124        if (nr != len) {
125            if (nr < 0)
126                return -1; 
127        }
128        chdr->name[len] = 0;
129        chdr->size -= len;
130    } else {
131        memmove(chdr->name, hdr->ar_name, sizeof(hdr->ar_name));
132
133        /* Strip trailing spaces, null terminate. */
134        for (p = chdr->name + sizeof(hdr->ar_name) - 1; *p == ' '; --p);
135        *++p = '\0';
136        chdr->lname = strlen(chdr->name);
137    }
138
139    /* limiting to 32-bit offsets */
140    chdr->addr = (uint32_t)lseek(fd, (off_t)0, SEEK_CUR);
141
142    return 0;
143}
144
145static void*
146unixfs_internal_init(const char* dmg, uint32_t flags, fs_endian_t fse,
147                     char** fsname, char** volname)
148{
149    int fd = -1;
150    if ((fd = open(dmg, O_RDONLY)) < 0) {
151        perror("open");
152        return NULL;
153    }
154
155    int err;
156    struct stat stbuf;
157    struct super_block* sb = (struct super_block*)0;
158    struct filsys* fs = (struct filsys*)0;
159
160    if ((err = fstat(fd, &stbuf)) != 0) {
161        perror("fstat");
162        goto out;
163    }
164
165    if (!S_ISREG(stbuf.st_mode) && !(flags & UNIXFS_FORCE)) {
166        err = EINVAL;
167        fprintf(stderr, "%s is not an ar image file\n", dmg);
168        goto out;
169    }
170
171    char magic[SARMAG];
172    if (read(fd, magic, SARMAG) != SARMAG) {
173        err = EIO;
174        fprintf(stderr, "failed to read magic from file\n");
175        goto out;
176    }
177
178    if (memcmp(magic, ARMAG, SARMAG) != 0) {
179        err = EINVAL;
180        fprintf(stderr, "%s is not an ar image file\n", dmg);
181        goto out;
182    }
183
184    sb = malloc(sizeof(struct super_block));
185    if (!sb) {
186        err = ENOMEM;
187        goto out;
188    }
189
190    assert(sizeof(struct filsys) <= BSIZE);
191
192    fs = calloc(1, BSIZE);
193    if (!fs) {
194        free(sb);
195        err = ENOMEM;
196        goto out;
197    }
198
199    unixfs = sb;
200
201    unixfs->s_flags = flags;
202    unixfs->s_endian = (fse == UNIXFS_FS_INVALID) ? UNIXFS_FS_LITTLE : fse;
203    unixfs->s_fs_info = (void*)fs;
204    unixfs->s_bdev = fd;
205
206    /* must initialize the inode layer before sanity checking */
207    if ((err = unixfs_inodelayer_init(sizeof(struct ar_node_info))) != 0)
208        goto out;
209
210    struct inode* rootip = unixfs_inodelayer_iget((ino_t)ROOTINO);
211    if (!rootip) {
212        fprintf(stderr, "*** fatal error: no root inode\n");
213        abort();
214    }
215
216    rootip->I_mode = S_IFDIR | 0755;
217    rootip->I_uid  = getuid();
218    rootip->I_gid  = getgid();
219    rootip->I_size = 2;
220    rootip->I_atime_sec = rootip->I_mtime_sec = rootip->I_ctime_sec =        time(0);
221
222    struct ar_node_info* rootai = (struct ar_node_info*)rootip->I_private;
223    rootai->ar_self = rootip;
224    rootai->ar_parent = NULL;
225    rootai->ar_children = NULL;
226    rootai->ar_next_sibling = NULL;
227
228    unixfs_inodelayer_isucceeded(rootip);
229
230    fs->s_fsize = (stbuf.st_size / BSIZE) + 1;
231    fs->s_files = 0;
232    fs->s_directories = 1 + 1 + 1;
233    fs->s_rootip = rootip;
234    fs->s_lastino = ROOTINO;
235
236    struct chdr ar;
237    ino_t parent_ino = ROOTINO;
238
239    for (;;) {
240
241        if (ancientfs_ar_readheader(fd, &ar) != 0)
242            break;
243
244        int missing = unixfs_internal_namei(parent_ino, ar.name, &stbuf);
245        if (!missing) /* duplicate */
246            goto next;
247
248        struct inode* ip = unixfs_inodelayer_iget((ino_t)(fs->s_lastino + 1));
249        if (!ip) {
250            fprintf(stderr, "*** fatal error: no inode for %llu\n",
251                   (ino64_t)(fs->s_lastino + 1));
252            abort();
253        }
254        ip->I_mode  = ar.mode;
255        ip->I_uid   = ar.uid;
256        ip->I_gid   = ar.gid;
257        ip->I_nlink = 1;
258        ip->I_size  = ar.size;
259        ip->I_atime_sec = ip->I_mtime_sec = ip->I_ctime_sec = ar.date;
260        ip->I_daddr[0] = ar.addr;
261
262        struct ar_node_info* ai = (struct ar_node_info*)ip->I_private;
263        ai->ar_name = malloc(ar.lname + 1);
264        if (!ai->ar_name) {
265            fprintf(stderr, "*** fatal error: cannot allocate memory\n");
266            abort();
267        }
268
269        memcpy(ai->ar_name, ar.name, ar.lname);
270        ai->ar_name[ar.lname] = '\0';
271        ai->ar_namelen = ar.lname;
272
273        ai->ar_self = ip;
274        ai->ar_children = NULL;
275        struct inode* parent_ip = unixfs_internal_iget(parent_ino);
276        parent_ip->I_size += 1;
277        ai->ar_parent = (struct ar_node_info*)(parent_ip->I_private);
278        ai->ar_next_sibling = ai->ar_parent->ar_children;
279        ai->ar_parent->ar_children = ai;
280        if (S_ISDIR(ip->I_mode)) {
281            fs->s_directories++;
282            parent_ino = fs->s_lastino + 1;
283            ip->I_size = 2;
284            ip->I_daddr[0] = 0;
285        } else {
286            fs->s_files++;
287            fs->s_lastino++;
288            unixfs_internal_iput(parent_ip);
289            unixfs_inodelayer_isucceeded(ip);
290           /* no put */
291        }
292
293        fs->s_lastino++;
294next:
295        (void)lseek(fd, (off_t)(ar.size + (ar.size & 1)), SEEK_CUR);
296    }
297
298    unixfs->s_statvfs.f_bsize = BSIZE;
299    unixfs->s_statvfs.f_frsize = BSIZE;
300    unixfs->s_statvfs.f_ffree = 0;
301    unixfs->s_statvfs.f_files = fs->s_files + fs->s_directories;
302    unixfs->s_statvfs.f_blocks = fs->s_fsize;
303    unixfs->s_statvfs.f_bfree = 0;
304    unixfs->s_statvfs.f_bavail = 0;
305    unixfs->s_dentsize = 1;
306    unixfs->s_statvfs.f_namemax = UNIXFS_MAXNAMLEN;
307
308    snprintf(unixfs->s_fsname, UNIXFS_MNAMELEN, "UNIX ar");
309
310    char* dmg_basename = basename((char*)dmg);
311    snprintf(unixfs->s_volname, UNIXFS_MAXNAMLEN, "%s (tape=%s)",
312             unixfs_fstype, (dmg_basename) ? dmg_basename : "Archive Image");
313
314    *fsname = unixfs->s_fsname;
315    *volname = unixfs->s_volname;
316
317out:
318    if (err) {
319        if (fd >= 0)
320            close(fd);
321        if (fs)
322            free(fs);
323        if (sb)
324            free(sb);
325        return NULL;
326    }
327
328    return sb;
329}
330
331static void
332unixfs_internal_fini(void* filsys)
333{
334    struct super_block* sb = (struct super_block*)filsys;
335    struct filsys* fs = (struct filsys*)sb->s_fs_info;
336    ino_t i = fs->s_lastino;
337    for (; i >= ROOTINO; i--) {
338        struct inode* tmp = unixfs_internal_iget(i);
339        if (tmp) {
340            struct ar_node_info* ai = (struct ar_node_info*)tmp->I_private;
341            if (ai->ar_name)
342                free(ai->ar_name);
343            unixfs_internal_iput(tmp);
344            unixfs_internal_iput(tmp);
345        }
346    }
347
348    unixfs_inodelayer_fini();
349
350    if (sb) {
351        if (sb->s_bdev >= 0)
352            close(sb->s_bdev);
353        sb->s_bdev = -1;
354        if (sb->s_fs_info)
355            free(sb->s_fs_info);
356    }
357}
358
359static off_t
360unixfs_internal_alloc(void)
361{
362    return (off_t)0;
363}
364
365static off_t
366unixfs_internal_bmap(struct inode* ip, off_t lblkno, int* error)
367{
368    return (off_t)0;
369}
370
371static int
372unixfs_internal_bread(off_t blkno, char* blkbuf)
373{
374    return EIO;
375}
376
377static struct inode*
378unixfs_internal_iget(ino_t ino)
379{
380    struct inode* ip = unixfs_inodelayer_iget(ino);
381    if (!ip) {
382        fprintf(stderr, "*** fatal error: no inode for %llu\n", (ino64_t)ino);
383        abort();
384    }
385
386    if (ip->I_initialized)
387        return ip;
388
389    unixfs_inodelayer_ifailed(ip);
390
391    return NULL; 
392}
393
394static void
395unixfs_internal_iput(struct inode* ip)
396{
397    unixfs_inodelayer_iput(ip);
398}
399
400static int
401unixfs_internal_igetattr(ino_t ino, struct stat* stbuf)
402{
403    struct inode* ip = unixfs_internal_iget(ino);
404    if (!ip)
405        return ENOENT;
406
407    unixfs_internal_istat(ip, stbuf);
408
409    unixfs_internal_iput(ip);
410
411    return 0;
412}
413
414static void
415unixfs_internal_istat(struct inode* ip, struct stat* stbuf)
416{
417    memcpy(stbuf, &ip->I_stat, sizeof(struct stat));
418}
419
420static int
421unixfs_internal_namei(ino_t parentino, const char* name, struct stat* stbuf)
422{
423    int ret = ENOENT;
424    stbuf->st_ino = 0;
425
426    size_t namelen = strlen(name);
427    if (namelen > UNIXFS_MAXNAMLEN)
428        return ENAMETOOLONG;
429
430    struct inode* dp = unixfs_internal_iget(parentino);
431    if (!dp)
432        return ENOENT;
433
434    if (!S_ISDIR(dp->I_mode)) {
435        ret = ENOTDIR;
436        goto out;
437    }
438
439    struct ar_node_info* child =
440        ((struct ar_node_info*)dp->I_private)->ar_children;;
441
442    if (!child) {
443        ret = ENOENT;
444        goto out;
445    }
446
447    int found = 0;
448
449    do {
450        size_t target_namelen = strlen((const char*)child->ar_name);
451        if ((namelen == target_namelen) &&
452            (memcmp(name, child->ar_name, target_namelen) == 0)) {
453            found = 1;
454            break;
455        }
456        child = child->ar_next_sibling;
457    } while (child);
458
459    if (found)
460        ret = unixfs_internal_igetattr((ino_t)child->ar_self->I_ino, stbuf);
461
462out:
463    unixfs_internal_iput(dp);
464
465    return ret;
466}
467
468static int
469unixfs_internal_nextdirentry(struct inode* dp, struct unixfs_dirbuf* dirbuf,
470                             off_t* offset, struct unixfs_direntry* dent)
471{
472    if (*offset >= dp->I_size)
473        return -1;
474
475    if (*offset < 2) {
476        int idx = 0;
477        dent->name[idx++] = '.';
478        dent->ino = ROOTINO;
479        if (*offset == 1) {
480            if (dp->I_ino != ROOTINO) {
481                struct inode* pdp = unixfs_internal_iget(dp->I_ino);
482                if (pdp) {
483                    dent->ino = pdp->I_ino;
484                    unixfs_internal_iput(pdp);
485                }
486            }
487            dent->name[idx++] = '.';
488        }
489        dent->name[idx++] = '\0';
490        goto out;
491    }
492
493    struct ar_node_info* child =
494        ((struct ar_node_info*)dp->I_private)->ar_children;;
495
496    off_t i;
497
498    for (i = 0; i < (*offset - 2); i++)
499        child = child->ar_next_sibling;
500
501    dent->ino = (ino_t)child->ar_self->I_ino;
502    size_t dirnamelen = min(child->ar_namelen, UNIXFS_MAXNAMLEN);
503    memcpy(dent->name, child->ar_name, dirnamelen);
504    dent->name[dirnamelen] = '\0';
505
506out:
507    *offset += 1;
508
509    return 0;
510}
511
512static ssize_t
513unixfs_internal_pbread(struct inode* ip, char* buf, size_t nbyte, off_t offset,
514                       int* error)
515{
516    off_t start = (off_t)ip->I_daddr[0];
517
518    /* caller already checked for bounds */
519
520    return pread(unixfs->s_bdev, buf, nbyte, start + offset);
521}
522
523static int
524unixfs_internal_readlink(ino_t ino, char path[UNIXFS_MAXPATHLEN])
525{
526    return ENOSYS;
527}
528
529static int
530unixfs_internal_sanitycheck(void* filsys, off_t disksize)
531{
532    return 0;
533}
534
535static int
536unixfs_internal_statvfs(struct statvfs* svb)
537{
538    memcpy(svb, &unixfs->s_statvfs, sizeof(struct statvfs));
539    return 0;
540}