/* iso9660 filesystem handling Copyright (C) 1996 Jakub Jelinek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __linux__ # include # include # include # include "silo.h" typedef int FILE; # include # include #else # include # include # include # include # include # include #endif #include "ext2fs/ext2fs.h" #include "9660.h" #define SUPISO (struct iso_superblock *)(fs->io->private_data) static struct ufs_superblock *ufs_read_super(ufs_filsys fs) { int i; struct iso_primary_descriptor *iso = (struct iso_primary_descriptor *) malloc (2048); for (i = 16; i < 100; i++) { if (io_channel_read_blk (fs->io, i, -2048, (char *)iso)) return 0; if (!strncmp (iso->id, ISO_STANDARD_ID, sizeof (iso->id))) } if (!usb) return 0; if (io_channel_read_blk (fs->io, UFS_SBLOCK/1024, -2048, (char *)usb)) return 0; if (usb->fs_magic != UFS_MAGIC) { /* XXX - replace hard-coded constant with a byte-swap macro */ if (usb->fs_magic == 0x54190100) { } return 0; } if (usb->fs_bsize != UFS_BSIZE) return 0; if (usb->fs_fsize != UFS_FSIZE) return 0; io_channel_set_blksize (fs->io, usb->fs_fsize); return usb; } int ufs_open (char *device, io_manager iom, ufs_filsys *pfs) { ufs_filsys fs; fs = (ufs_filsys) malloc (sizeof (struct struct_ext2_filsys)); if (!fs) return -1; if (iom->open (device, 0, &fs->io)) return -1; io_channel_set_blksize (fs->io, 1024); fs->io->private_data = ufs_read_super(fs); if (!fs->io->private_data) return -1; *pfs = fs; return 0; } int ufs_read_inode (ufs_filsys fs, ino_t inode, struct ufs_inode *ui) { struct ufs_inode *ufsip; struct ufs_superblock *sb = SUPUFS; char *buffer; if (inode < 2 || inode > (sb->fs_ncg * sb->fs_ipg - 1)) return -1; ufsip = (struct ufs_inode *) malloc (1024); buffer = (char *) ufsip; if (io_channel_read_blk (fs->io, cgimin (ino2cg(inode)) + (inode % sb->fs_ipg) / (sb->fs_inopb / sb->fs_frag), -1024, (char *)ufsip)) { printf ("Couldn't read inode\n"); return -1; } ufsip += (inode%(sb->fs_inopb / sb->fs_frag)); *ui = *ufsip; free (buffer); return 0; } static int block_bmap (ufs_filsys fs, int block, int nr) { struct ufs_superblock *sb = SUPUFS; int tmp = nr >> (sb->fs_fshift - 2); static int lastbuftmp = -1; static __u32 *lastdata = 0; nr &= ~(sb->fs_fmask) >> 2; if (block + tmp != lastbuftmp) { if (!lastdata) lastdata = (__u32 *) malloc (sb->fs_fsize); lastbuftmp = block + tmp; if (io_channel_read_blk (fs->io, block + tmp, -sb->fs_fsize, lastdata)) return 0; } return lastdata[nr]; } static int ufs_bmap (ufs_filsys fs, ino_t inode, struct ufs_inode *ui, int block) { struct ufs_superblock *sb = SUPUFS; int i; int addr_per_block = sb->fs_bsize >> 2; int addr_per_block_bits = sb->fs_bshift - 2; int lbn = block >> (sb->fs_bshift - sb->fs_fshift); int boff = (block & ((sb->fs_fmask - sb->fs_bmask) >> sb->fs_fshift)); if (lbn < 0) return 0; if (lbn >= UFS_NDADDR + addr_per_block + (1 << (addr_per_block_bits * 2)) + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) return 0; if (lbn < UFS_NDADDR) return ui->ui_db[lbn] + boff; lbn -= UFS_NDADDR; if (lbn < addr_per_block) { i = ui->ui_ib [0]; if (!i) return 0; return block_bmap (fs, i, lbn) + boff; } lbn -= addr_per_block; if (lbn < (1 << (addr_per_block_bits * 2))) { i = ui->ui_ib [1]; if (!i) return 0; i = block_bmap (fs, i, lbn >> addr_per_block_bits); if (!i) return 0; return block_bmap (fs, i, lbn & (addr_per_block-1)) + boff; } lbn -= (1 << (addr_per_block_bits * 2)); i = ui->ui_ib [2]; if (!i) return 0; i = block_bmap (fs, i, lbn >> (addr_per_block_bits * 2)); if (!i) return 0; i = block_bmap (fs, i, (lbn >> addr_per_block_bits) & (addr_per_block - 1)); if (!i) return 0; return block_bmap (fs, i, lbn & (addr_per_block-1)) + boff; } static int ufs_match (int len, const char *const name, struct ufs_direct * d) { if (!d || len > UFS_MAXNAMLEN) return 0; if (!len && (d->d_namlen == 1) && (d->d_name[0] == '.') && (d->d_name[1] == '\0')) return 1; if (len != d->d_namlen) return 0; return !memcmp(name, d->d_name, len); } static int ufs_lookup (ufs_filsys fs, ino_t dir, struct ufs_inode *dirui, const char *name, int len, ino_t *result) { unsigned long int lfragno, fragno; struct ufs_direct * d; char buffer [8192]; struct ufs_superblock *sb = SUPUFS; for (lfragno = 0; lfragno < (dirui->ui_blocks)>>1; lfragno++) { fragno = ufs_bmap(fs, dir, dirui, lfragno); if (!fragno) return -1; if (io_channel_read_blk (fs->io, fragno, -sb->fs_fsize, buffer)) { printf ("Couldn't read directory\n"); return -1; } d = (struct ufs_direct *)buffer; while (((char *)d - buffer + d->d_reclen) <= sb->fs_fsize) { if (!d->d_reclen || !d->d_namlen) break; if (d->d_namlen == len && ufs_match(len, name, d)) { *result = d->d_ino; return 0; } d = (struct ufs_direct *)((char *)d + d->d_reclen); } } return -1; } static int link_count = 0; static int open_namei(ufs_filsys, const char *, ino_t *, ino_t); static int ufs_follow_link(ufs_filsys fs, ino_t dir, ino_t inode, struct ufs_inode *ui, ino_t *res_inode) { unsigned long int block; int error; char *link; char buffer[1024]; if (!S_ISLNK(ui->ui_mode)) { *res_inode = inode; return 0; } if (link_count > 5) { printf ("Symlink loop\n"); return -1; /* Loop */ } if (ui->ui_blocks) { /* read the link from disk */ block = ufs_bmap(fs, inode, ui, 0); if (io_channel_read_blk (fs->io, block, -1024, buffer)) { printf ("Couldn't readlink\n"); return -1; } link = buffer; } else { /* fast symlink */ link = (char *)&(ui->ui_db[0]); } link_count++; error = open_namei (fs, link, res_inode, dir); link_count--; return error; } static int dir_namei(ufs_filsys fs, const char *pathname, int *namelen, const char **name, ino_t base, ino_t *res_inode) { char c; const char *thisname; int len; struct ufs_inode ub; ino_t inode; if ((c = *pathname) == '/') { base = (ino_t)fs->private; pathname++; } if (ufs_read_inode (fs, base, &ub)) return -1; while (1) { thisname = pathname; for(len=0;(c = *(pathname++))&&(c != '/');len++); if (!c) break; if (ufs_lookup (fs, base, &ub, thisname, len, &inode)) return -1; if (ufs_read_inode (fs, inode, &ub)) return -1; if (ufs_follow_link (fs, base, inode, &ub, &base)) return -1; if (base != inode && ufs_read_inode (fs, base, &ub)) return -1; } *name = thisname; *namelen = len; *res_inode = base; return 0; } static int open_namei(ufs_filsys fs, const char *pathname, ino_t *res_inode, ino_t base) { const char *basename; int namelen,error; ino_t dir, inode; struct ufs_inode ub; if (dir_namei(fs, pathname, &namelen, &basename, base, &dir)) return -1; if (!namelen) { /* special case: '/usr/' etc */ *res_inode=dir; return 0; } if (ufs_read_inode (fs, dir, &ub)) return -1; if (ufs_lookup (fs, dir, &ub, basename, namelen, &inode)) return -1; if (ufs_read_inode (fs, inode, &ub)) return -1; if (ufs_follow_link (fs, dir, inode, &ub, &inode)) return -1; *res_inode = inode; return 0; } int ufs_namei (ufs_filsys fs, ino_t root, ino_t cwd, char *filename, ino_t *inode) { fs->private = (void *)root; link_count = 0; return open_namei (fs, filename, inode, cwd); } void ufs_close(ufs_filsys fs) { free (fs->io); free (fs); } int ufs_block_iterate(ufs_filsys fs, ino_t inode, int (*func)(ufs_filsys, blk_t *, int, void *), void *private) { struct ufs_inode ub; int i; blk_t nr; int frags; struct ufs_superblock *sb = SUPUFS; if (ufs_read_inode (fs, inode, &ub)) return -1; frags = (ub.ui_size.val[1] + sb->fs_fsize - 1) / sb->fs_fsize; for (i = 0; i < frags; i++) { nr = ufs_bmap (fs, inode, &ub, i); if (!nr) return -1; switch ((*func) (fs, &nr, i, private)) { case BLOCK_ABORT: case BLOCK_ERROR: return -1; } } return 0; }