PageRenderTime 69ms CodeModel.GetById 2ms app.highlight 62ms RepoModel.GetById 1ms app.codeStats 0ms

/filesystems/unixfs/sysvfs/unixfs_sysvfs.c

http://macfuse.googlecode.com/
C | 397 lines | 310 code | 80 blank | 7 comment | 58 complexity | 56a082cdd2a2a73ca6009dcbb631b50b MD5 | raw file
  1/*
  2 * SYSV File System Famiy for MacFUSE
  3 * Copyright (c) 2008 Amit Singh
  4 * http://osxbook.com
  5 */
  6
  7#include "sysvfs.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 System V", sysv);
 20
 21static void*
 22unixfs_internal_init(const char* dmg, uint32_t flags, __unused fs_endian_t fse,
 23                     char** fsname, char** volname)
 24{
 25    int fd = -1;
 26
 27    if ((fd = open(dmg, O_RDONLY)) < 0) {
 28        perror("open");
 29        return NULL;
 30    }
 31
 32    int err;
 33    struct stat stbuf;
 34    struct super_block* sb = (struct super_block*)0;
 35
 36    if ((err = fstat(fd, &stbuf)) != 0) {
 37        perror("fstat");
 38        goto out;
 39    }
 40
 41    if (!S_ISREG(stbuf.st_mode) && !(flags & UNIXFS_FORCE)) {
 42        err = EINVAL;
 43        fprintf(stderr, "%s is not a disk image file\n", dmg);
 44        goto out;
 45    }
 46
 47    if ((err = unixfs_inodelayer_init(sizeof(struct sysv_inode_info))) != 0)
 48        goto out;
 49
 50    sb = sysv_fill_super(fd, (void*)0, 1 /* silent */);
 51    if (!sb) {
 52        err = EINVAL;
 53        goto out;
 54    }
 55
 56    struct sysv_sb_info* sbi = SYSV_SB(sb);
 57
 58    unixfs = sb;
 59    unixfs->s_flags = flags;
 60
 61    unixfs->s_statvfs.f_bsize   = max(PAGE_SIZE, sb->s_blocksize);
 62    unixfs->s_statvfs.f_frsize  = sb->s_blocksize;
 63    unixfs->s_statvfs.f_blocks  = sbi->s_ndatazones;
 64    unixfs->s_statvfs.f_bavail  = sysv_count_free_blocks(sb);
 65    unixfs->s_statvfs.f_bfree   = unixfs->s_statvfs.f_bavail;
 66    unixfs->s_statvfs.f_files   = sbi->s_ninodes;
 67    unixfs->s_statvfs.f_ffree   = sysv_count_free_inodes(sb);
 68    unixfs->s_statvfs.f_namemax = SYSV_NAMELEN;
 69    unixfs->s_dentsize = 0;
 70
 71    snprintf(unixfs->s_fsname, UNIXFS_MNAMELEN, "%s", sysv_flavor(sbi->s_type));
 72    snprintf(unixfs->s_volname, UNIXFS_MAXNAMLEN, "%s (%s)",
 73             unixfs_fstype, sysv_flavor(sbi->s_type));
 74
 75    *fsname = unixfs->s_fsname;
 76    *volname = unixfs->s_volname;
 77
 78out:
 79    if (err) {
 80        if (fd > 0)
 81            close(fd);
 82        if (sb) {
 83            struct sysv_sb_info* sbi = SYSV_SB(sb);
 84            if (sbi) {
 85                void* bh1 = sbi->s_bh1;
 86                void* bh2 = sbi->s_bh2;
 87                if (bh1) {
 88                    if (bh1 == bh2)
 89                        bh2 = NULL;
 90                    brelse(bh1);
 91                }
 92                if (bh2)
 93                    brelse(bh2);
 94                free(sbi);
 95            }
 96            free(sb);
 97        }
 98    }
 99
100    return sb;
101}
102
103static void
104unixfs_internal_fini(void* filsys)
105{
106    unixfs_inodelayer_fini();
107
108    struct super_block* sb = (struct super_block*)filsys;
109
110    if (sb) {
111        struct sysv_sb_info* sbi = SYSV_SB(sb);
112        if (sbi) {
113            void* bh1 = sbi->s_bh1;
114            void* bh2 = sbi->s_bh2;
115            if (bh1) {
116                if (bh1 == bh2)
117                    bh2 = NULL;
118                brelse(bh1);
119            }
120            if (bh2)
121                brelse(bh2);
122            free(sbi);
123        }
124        free(sb);
125    }
126}
127
128static off_t
129unixfs_internal_alloc(void)
130{
131    return (off_t)0;
132}
133
134static off_t
135unixfs_internal_bmap(struct inode* ip, off_t lblkno, int* error)
136{
137    off_t result;
138    *error = sysv_get_block(ip, lblkno, &result);
139    return result;
140}
141
142static int
143unixfs_internal_bread(off_t blkno, char* blkbuf)
144{
145    struct super_block* sb = unixfs;
146
147    if (pread(sb->s_bdev, blkbuf, sb->s_blocksize,
148              blkno * (off_t)(sb->s_blocksize)) != sb->s_blocksize)
149        return EIO;
150
151    return 0;
152}
153
154struct inode*
155unixfs_internal_iget(ino_t ino)
156{
157   if (ino == MACFUSE_ROOTINO)
158        ino = SYSV_ROOT_INO;
159
160   struct super_block* sb = unixfs;
161   struct sysv_sb_info* sbi = SYSV_SB(sb);
162
163   if (!ino || ino > sbi->s_ninodes) {
164       fprintf(stderr, "bad inode number: %llu\n", ino);
165       return NULL;
166    }
167
168    struct inode* inode = unixfs_inodelayer_iget(ino);
169    if (!inode) {
170        fprintf(stderr, "*** fatal error: no inode for %llu\n", ino);
171        abort();
172    }
173
174    if (inode->I_initialized)
175        return inode;
176
177    struct buffer_head  bh;
178    struct sysv_dinode* raw_inode = sysv_raw_inode(sb, ino, &bh);
179    if (!raw_inode) {
180        fprintf(stderr, "major problem: failed to read inode %llu\n", ino);
181        unixfs_inodelayer_ifailed(inode);
182        goto bad_inode;
183    }
184
185    struct sysv_inode_info *si = SYSV_I(inode);
186
187    inode->I_ino = ino;
188
189    /* SystemV FS: kludge permissions if ino==SYSV_ROOT_INO ?? */
190
191    inode->I_mode = fs16_to_host(unixfs->s_endian, raw_inode->di_mode);
192
193    inode->I_uid   = (uid_t)fs16_to_host(unixfs->s_endian, raw_inode->di_uid);
194    inode->I_gid   = (gid_t)fs16_to_host(unixfs->s_endian, raw_inode->di_gid);
195    inode->I_nlink = fs16_to_host(unixfs->s_endian, raw_inode->di_nlink);
196    inode->I_size  = fs32_to_host(unixfs->s_endian, raw_inode->di_size);
197
198    inode->I_atime.tv_sec = fs32_to_host(unixfs->s_endian, raw_inode->di_atime);
199    inode->I_mtime.tv_sec = fs32_to_host(unixfs->s_endian, raw_inode->di_mtime);
200    inode->I_ctime.tv_sec = fs32_to_host(unixfs->s_endian, raw_inode->di_ctime);
201    inode->I_ctime.tv_nsec = 0;
202    inode->I_atime.tv_nsec = 0;
203    inode->I_mtime.tv_nsec = 0;
204
205    inode->I_sb = unixfs;
206    inode->I_blkbits = sb->s_blocksize_bits;
207
208    unsigned int block;
209    for (block = 0; block < (10 + 1 + 1 + 1); block++)
210        sysv_read3byte(sbi, &raw_inode->di_data[3 * block],
211                       (u8*)&si->i_data[block]);
212
213    if (S_ISCHR(inode->I_mode) || S_ISBLK(inode->I_mode)) {
214        uint32_t rdev = fs32_to_host(unixfs->s_endian, si->i_data[0]);
215        inode->I_rdev = makedev((rdev >> 8) & 255, rdev & 255);
216    }
217
218    si->i_dir_start_lookup = 0;
219
220    unixfs_inodelayer_isucceeded(inode);
221
222    return inode;
223
224bad_inode:
225    return NULL;
226}
227
228static void
229unixfs_internal_iput(struct inode* ip)
230{
231    unixfs_inodelayer_iput(ip);
232}
233
234static int
235unixfs_internal_igetattr(ino_t ino, struct stat* stbuf)
236{
237    if (ino == MACFUSE_ROOTINO)
238        ino = SYSV_ROOT_INO;
239
240    struct inode* ip = unixfs_internal_iget(ino);
241    if (!ip)
242        return ENOENT;
243
244    unixfs_internal_istat(ip, stbuf);
245
246    unixfs_internal_iput(ip);
247
248    return 0;
249}
250
251static void
252unixfs_internal_istat(struct inode* ip, struct stat* stbuf)
253{
254    memcpy(stbuf, &ip->I_stat, sizeof(struct stat));
255}
256
257static int
258unixfs_internal_namei(ino_t parentino, const char* name, struct stat* stbuf)
259{
260    if (parentino == MACFUSE_ROOTINO)
261        parentino = SYSV_ROOT_INO;
262
263    stbuf->st_ino = ENOENT;
264
265    struct inode* dir = unixfs_internal_iget(parentino);
266    if (!dir)
267        return ENOENT;
268
269    if (!S_ISDIR(dir->I_mode)) {
270        unixfs_internal_iput(dir);
271        return ENOTDIR;
272    }
273
274    int ret = ENOENT, found = 0;
275
276    unsigned long namelen = strlen(name);
277    unsigned long start, n;
278    unsigned long npages = sysv_dir_pages(dir);
279    struct sysv_dir_entry* de;
280    char page[PAGE_SIZE];
281    char* kaddr = NULL;
282
283    start = SYSV_I(dir)->i_dir_start_lookup;
284    if (start >= npages)
285        start = 0;
286    n = start;
287
288    do {
289        int error = sysv_get_page(dir, n, page);
290        if (!error) {
291            kaddr = (char*)page;
292            de = (struct sysv_dir_entry*) kaddr;
293            kaddr += PAGE_CACHE_SIZE - SYSV_DIRSIZE;
294            for ( ; (char*) de <= kaddr ; de++) {
295                if (!de->inode)
296                    continue;
297                if (sysv_namecompare(namelen, SYSV_NAMELEN, name, de->name)) {
298                    found = 1;
299                    goto found;
300                }
301            }
302        }
303
304        if (++n >= npages)
305            n = 0;
306    } while (n != start);
307
308found:
309
310    if (found)
311        SYSV_I(dir)->i_dir_start_lookup = n;
312
313    unixfs_internal_iput(dir);
314
315    if (found)
316        ret = unixfs_internal_igetattr((ino_t)fs16_to_host(unixfs->s_endian,
317                                       de->inode), stbuf);
318
319    return ret;
320}
321
322int
323unixfs_internal_nextdirentry(struct inode* dp, struct unixfs_dirbuf* dirbuf,
324                             off_t* offset, struct unixfs_direntry* dent)
325{
326    return sysv_next_direntry(dp, dirbuf, offset, dent);
327
328}
329
330static ssize_t
331unixfs_internal_pbread(struct inode* ip, char* buf, size_t nbyte, off_t offset,
332                       int* error)
333{
334    char* p = buf;
335    ssize_t done = 0;
336    ssize_t tomove = 0;
337    ssize_t remaining = nbyte;
338    char page[PAGE_SIZE];
339    sector_t beginpgno = offset >> PAGE_CACHE_SHIFT;
340
341    while (remaining > 0) { /* page sized reads */
342        *error = sysv_get_page(ip, beginpgno, page);
343        if (*error)
344            break;
345        tomove = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining;
346        memcpy(p, page, tomove);
347        remaining -= tomove;
348        done += tomove;
349        p += tomove;
350        beginpgno++;
351    }
352
353    if ((done == 0) && *error)
354        return -1;
355    
356    return done;
357}
358
359static int
360unixfs_internal_readlink(ino_t ino, char path[UNIXFS_MAXPATHLEN])
361{
362    struct inode* ip = unixfs_internal_iget(ino);
363    if (!ip)
364        return ENOENT;
365
366    int error = 0;
367
368    size_t linklen =
369      (ip->I_size > UNIXFS_MAXPATHLEN - 1) ? UNIXFS_MAXPATHLEN - 1: ip->I_size;
370
371    char page[PAGE_SIZE];
372    error = sysv_get_page(ip, (off_t)0, page);
373    if (error)
374        goto out;
375    memcpy(path, page, linklen);
376
377    path[linklen] = '\0';
378
379out:
380    unixfs_internal_iput(ip);
381
382    return error;
383}
384
385int
386unixfs_internal_sanitycheck(void* filsys, off_t disksize)
387{
388    /* handled elsewhere */
389    return 0;
390}
391
392static int
393unixfs_internal_statvfs(struct statvfs* svb)
394{
395    memcpy(svb, &unixfs->s_statvfs, sizeof(struct statvfs));
396    return 0;
397}