/* SunOS UFS Interface for SILO filesystem access routines Copyright (C) 1996 Adrian Rodriguez 1996 Jakub Jelinek 2001 Ben Collins 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #ifdef __GLIBC__ # define _LINUX_TIME_H #endif struct inode { unsigned int blah; }; #include /* Reuse and abuse */ typedef ext2_filsys ufs_filsys; ino_t inode = 0; #define ufsi_size(x) ((unsigned int)((x)->ui_size)) #define ufsi_db(x) ((unsigned int *)((x)->ui_u2.ui_addr.ui_db)) #define ufsi_ib(x) ((unsigned int *)((x)->ui_u2.ui_addr.ui_ib)) #define ufsd_namlen(x) ((unsigned char)((x)->d_u.d_44.d_namlen)) struct ufs_superblock_full { struct ufs_super_block_first first; struct ufs_super_block_second second; struct ufs_super_block_third third; }; #define ufs_superblock ufs_super_block_first #define ufs_direct ufs_dir_entry #ifndef S_ISLNK #include #endif #include #define SUPUFS (struct ufs_superblock *)(fs->io->private_data) #define cgstart(cg) ((sb->fs_fpg * (cg)) + sb->fs_cgoffset * ((cg) & ~(sb->fs_cgmask))) #define cgimin(cg) (cgstart(cg) + sb->fs_iblkno) #define cgdmin(cg) (cgstart(cg) + sb->fs_dblkno) #define ino2cg(ino) ((ino) / sb->fs_ipg) static char *get_archstr(void) { char *p = "sun4c"; switch (silo_get_architecture()) { case sun4: p = "sun4"; break; case sun4c: p = "sun4c"; break; case sun4m: p = "sun4m"; break; case sun4d: p = "sun4d"; break; case sun4e: p = "sun4e"; break; case sun4u: p = "sun4u"; break; case sun4p: p = "sun4p"; break; default: break; } return p; } static struct ufs_superblock_full *ufs_read_super(ufs_filsys fs) { struct ufs_superblock_full *usb; usb = (struct ufs_superblock_full *) malloc (2048); if (!usb) return 0; if (io_channel_read_blk (fs->io, UFS_SBLOCK/1024, -2048, (char *)usb)) return 0; if (usb->third.fs_magic != UFS_MAGIC) { return 0; } if (usb->first.fs_bsize != UFS_BSIZE) return 0; if (usb->first.fs_fsize != UFS_FSIZE) return 0; io_channel_set_blksize (fs->io, usb->first.fs_fsize); return usb; } static 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 ufsi_db(ui)[lbn] + boff; lbn -= UFS_NDADDR; if (lbn < addr_per_block) { i = ufsi_ib(ui)[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 = ufsi_ib(ui)[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 = ufsi_ib(ui)[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 && (ufsd_namlen(d) == 1) && (d->d_name[0] == '.') && (d->d_name[1] == '\0')) return 1; if (len != ufsd_namlen(d)) 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 || !ufsd_namlen(d)) break; if (ufsd_namlen(d) == 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 *)&(ufsi_db(ui)[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 = root; 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; 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; } static int ufs_namei (ufs_filsys fs, ino_t root, ino_t cwd, const char *filename, ino_t *inode) { link_count = 0; return open_namei (fs, filename, inode, cwd); } static void ufs_close(void) { free (fs->io); free (fs); } static int ufs_block_iterate(void) { struct ufs_inode ub; int i; blk_t nr; int frags; struct ufs_superblock *sb = SUPUFS; if (ufs_read_inode (fs, inode, &ub)) return 0; frags = (ufsi_size(&ub) + sb->fs_fsize - 1) / sb->fs_fsize; for (i = 0; i < frags; i++) { nr = ufs_bmap (fs, inode, &ub, i); if (!nr) return 0; switch (dump_block(&nr, i)) { case BLOCK_ABORT: case BLOCK_ERROR: return 0; } } return dump_finish(); } struct fs_ops ufs_fs_ops; static int namei_follow_ufs (const char *filename) { int syspkg = 0; cwd = root; ufs_fs_ops.have_inode = 0; if (solaris) { if (!ufs_namei (fs, root, root, "/platform", &cwd)) { if (!ufs_namei (fs, root, cwd, get_syspackage(), &inode)) { cwd = inode; syspkg = 1; } else { if (!ufs_namei (fs, root, cwd, get_archstr(), &inode)) cwd = inode; } } if (cwd != root && *filename == '/') filename++; } if (ufs_namei (fs, root, cwd, filename, &inode)) { if (syspkg) { syspkg = 0; ufs_namei (fs, root, root, "/platform", &cwd); if (!ufs_namei (fs, root, cwd, get_archstr(), &inode)) { cwd = inode; if (!ufs_namei (fs, root, cwd, filename, &inode)) syspkg = 1; } } if (!syspkg) return 1; } if (solaris) { ino_t sinode; if (ufs_namei (fs, root, cwd, "ufsboot", &sinode)) { printf ("\nCannot find Solaris kernel bootloader `ufsboot'. Will try to load it,\n" "but it may fail\n"); solaris = 0; } else inode = sinode; } ufs_fs_ops.have_inode = 0; return 0; } static int open_ufs (char *device) { fs = (ufs_filsys) malloc (sizeof (struct struct_ext2_filsys)); if (!fs) return 0; if (((struct struct_io_manager *)(silo_io_manager))->open (device, 0, &fs->io)) return 0; io_channel_set_blksize (fs->io, 1024); if (!(fs->io->private_data = ufs_read_super(fs))) return 0; root = UFS_ROOTINO; solaris = 1; return 1; } static int ino_size_ufs (void) { struct ufs_inode ui; if (ufs_read_inode (fs, inode, &ui)) return 0; /* Hope nobody is so stupid to load 4GB+ * kernel into core :)))) */ return ufsi_size(&ui); } static void print_error_ufs (int error_val) { printf("Unknown ufs error"); } struct fs_ops ufs_fs_ops = { name: "SunOS UFS", open: open_ufs, ls: NULL/*ls_ufs*/, dump: ufs_block_iterate, close: ufs_close, ino_size: ino_size_ufs, print_error: print_error_ufs, namei_follow: namei_follow_ufs, have_inode: 0, }; .