/* ufs.c - Unix File System */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2004,2005,2007,2008,2009 Free Software Foundation, Inc. * * VAS_EBOOT 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 3 of the License, or * (at your option) any later version. * * VAS_EBOOT 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 VAS_EBOOT. If not, see . */ #include #include #include #include #include #include #include #include #include VAS_EBOOT_MOD_LICENSE ("GPLv3+"); #ifdef MODE_UFS2 #define VAS_EBOOT_UFS_MAGIC 0x19540119 #else #define VAS_EBOOT_UFS_MAGIC 0x11954 #endif #define VAS_EBOOT_UFS_INODE 2 #define VAS_EBOOT_UFS_FILETYPE_DIR 4 #define VAS_EBOOT_UFS_FILETYPE_LNK 10 #define VAS_EBOOT_UFS_MAX_SYMLNK_CNT 8 #define VAS_EBOOT_UFS_DIRBLKS 12 #define VAS_EBOOT_UFS_INDIRBLKS 3 #define VAS_EBOOT_UFS_ATTR_TYPE 0160000 #define VAS_EBOOT_UFS_ATTR_FILE 0100000 #define VAS_EBOOT_UFS_ATTR_DIR 0040000 #define VAS_EBOOT_UFS_ATTR_LNK 0120000 #define VAS_EBOOT_UFS_VOLNAME_LEN 32 #ifdef MODE_BIGENDIAN #define VasEBoot_ufs_to_cpu16 VasEBoot_be_to_cpu16 #define VasEBoot_ufs_to_cpu32 VasEBoot_be_to_cpu32 #define VasEBoot_ufs_to_cpu64 VasEBoot_be_to_cpu64 #define VasEBoot_cpu_to_ufs32_compile_time VasEBoot_cpu_to_be32_compile_time #else #define VasEBoot_ufs_to_cpu16 VasEBoot_le_to_cpu16 #define VasEBoot_ufs_to_cpu32 VasEBoot_le_to_cpu32 #define VasEBoot_ufs_to_cpu64 VasEBoot_le_to_cpu64 #define VasEBoot_cpu_to_ufs32_compile_time VasEBoot_cpu_to_le32_compile_time #endif #ifdef MODE_UFS2 typedef VasEBoot_uint64_t VasEBoot_ufs_blk_t; static inline VasEBoot_disk_addr_t VasEBoot_ufs_to_cpu_blk (VasEBoot_ufs_blk_t blk) { return VasEBoot_ufs_to_cpu64 (blk); } #else typedef VasEBoot_uint32_t VasEBoot_ufs_blk_t; static inline VasEBoot_disk_addr_t VasEBoot_ufs_to_cpu_blk (VasEBoot_ufs_blk_t blk) { return VasEBoot_ufs_to_cpu32 (blk); } #endif /* Calculate in which group the inode can be found. */ #define UFS_BLKSZ(sblock) (VasEBoot_ufs_to_cpu32 (sblock->bsize)) #define UFS_LOG_BLKSZ(sblock) (data->log2_blksz) #ifdef MODE_UFS2 #define INODE_ENDIAN(data,field,bits1,bits2) VasEBoot_ufs_to_cpu##bits2 (data->inode.field) #else #define INODE_ENDIAN(data,field,bits1,bits2) VasEBoot_ufs_to_cpu##bits1 (data->inode.field) #endif #define INODE_SIZE(data) VasEBoot_ufs_to_cpu64 (data->inode.size) #define INODE_MODE(data) VasEBoot_ufs_to_cpu16 (data->inode.mode) #ifdef MODE_UFS2 #define LOG_INODE_BLKSZ 3 #else #define LOG_INODE_BLKSZ 2 #endif #ifdef MODE_UFS2 #define UFS_INODE_PER_BLOCK 2 #else #define UFS_INODE_PER_BLOCK 4 #endif #define INODE_DIRBLOCKS(data,blk) INODE_ENDIAN \ (data,blocks.dir_blocks[blk],32,64) #define INODE_INDIRBLOCKS(data,blk) INODE_ENDIAN \ (data,blocks.indir_blocks[blk],32,64) /* The blocks on which the superblock can be found. */ static int sblocklist[] = { 128, 16, 0, 512, -1 }; struct VasEBoot_ufs_sblock { VasEBoot_uint8_t unused[16]; /* The offset of the inodes in the cylinder group. */ VasEBoot_uint32_t inoblk_offs; VasEBoot_uint8_t unused2[4]; /* The start of the cylinder group. */ VasEBoot_uint32_t cylg_offset; VasEBoot_uint32_t cylg_mask; VasEBoot_uint32_t mtime; VasEBoot_uint8_t unused4[12]; /* The size of a block in bytes. */ VasEBoot_int32_t bsize; VasEBoot_uint8_t unused5[48]; /* The size of filesystem blocks to disk blocks. */ VasEBoot_uint32_t log2_blksz; VasEBoot_uint8_t unused6[40]; VasEBoot_uint32_t uuidhi; VasEBoot_uint32_t uuidlow; VasEBoot_uint8_t unused7[32]; /* Inodes stored per cylinder group. */ VasEBoot_uint32_t ino_per_group; /* The frags per cylinder group. */ VasEBoot_uint32_t frags_per_group; VasEBoot_uint8_t unused8[488]; /* Volume name for UFS2. */ VasEBoot_uint8_t volume_name[VAS_EBOOT_UFS_VOLNAME_LEN]; VasEBoot_uint8_t unused9[360]; VasEBoot_uint64_t mtime2; VasEBoot_uint8_t unused10[292]; /* Magic value to check if this is really a UFS filesystem. */ VasEBoot_uint32_t magic; }; #ifdef MODE_UFS2 /* UFS inode. */ struct VasEBoot_ufs_inode { VasEBoot_uint16_t mode; VasEBoot_uint16_t nlinks; VasEBoot_uint32_t uid; VasEBoot_uint32_t gid; VasEBoot_uint32_t blocksize; VasEBoot_uint64_t size; VasEBoot_int64_t nblocks; VasEBoot_uint64_t atime; VasEBoot_uint64_t mtime; VasEBoot_uint64_t ctime; VasEBoot_uint64_t create_time; VasEBoot_uint32_t atime_usec; VasEBoot_uint32_t mtime_usec; VasEBoot_uint32_t ctime_usec; VasEBoot_uint32_t create_time_sec; VasEBoot_uint32_t gen; VasEBoot_uint32_t kernel_flags; VasEBoot_uint32_t flags; VasEBoot_uint32_t extsz; VasEBoot_uint64_t ext[2]; union { struct { VasEBoot_uint64_t dir_blocks[VAS_EBOOT_UFS_DIRBLKS]; VasEBoot_uint64_t indir_blocks[VAS_EBOOT_UFS_INDIRBLKS]; } blocks; VasEBoot_uint8_t symlink[(VAS_EBOOT_UFS_DIRBLKS + VAS_EBOOT_UFS_INDIRBLKS) * 8]; }; VasEBoot_uint8_t unused[24]; } VAS_EBOOT_PACKED; #else /* UFS inode. */ struct VasEBoot_ufs_inode { VasEBoot_uint16_t mode; VasEBoot_uint16_t nlinks; VasEBoot_uint16_t uid; VasEBoot_uint16_t gid; VasEBoot_uint64_t size; VasEBoot_uint32_t atime; VasEBoot_uint32_t atime_usec; VasEBoot_uint32_t mtime; VasEBoot_uint32_t mtime_usec; VasEBoot_uint32_t ctime; VasEBoot_uint32_t ctime_usec; union { struct { VasEBoot_uint32_t dir_blocks[VAS_EBOOT_UFS_DIRBLKS]; VasEBoot_uint32_t indir_blocks[VAS_EBOOT_UFS_INDIRBLKS]; } blocks; VasEBoot_uint8_t symlink[(VAS_EBOOT_UFS_DIRBLKS + VAS_EBOOT_UFS_INDIRBLKS) * 4]; }; VasEBoot_uint32_t flags; VasEBoot_uint32_t nblocks; VasEBoot_uint32_t gen; VasEBoot_uint32_t unused; VasEBoot_uint8_t pad[12]; } VAS_EBOOT_PACKED; #endif /* Directory entry. */ struct VasEBoot_ufs_dirent { VasEBoot_uint32_t ino; VasEBoot_uint16_t direntlen; union { VasEBoot_uint16_t namelen; struct { VasEBoot_uint8_t filetype_bsd; VasEBoot_uint8_t namelen_bsd; }; }; } VAS_EBOOT_PACKED; /* Information about a "mounted" ufs filesystem. */ struct VasEBoot_ufs_data { struct VasEBoot_ufs_sblock sblock; VasEBoot_disk_t disk; struct VasEBoot_ufs_inode inode; int ino; int linknest; int log2_blksz; }; static VasEBoot_dl_t my_mod; /* Forward declaration. */ static VasEBoot_err_t VasEBoot_ufs_find_file (struct VasEBoot_ufs_data *data, const char *path); static VasEBoot_disk_addr_t VasEBoot_ufs_get_file_block (struct VasEBoot_ufs_data *data, VasEBoot_disk_addr_t blk) { unsigned long indirsz; int log2_blksz, log_indirsz; /* Direct. */ if (blk < VAS_EBOOT_UFS_DIRBLKS) return INODE_DIRBLOCKS (data, blk); log2_blksz = VasEBoot_ufs_to_cpu32 (data->sblock.log2_blksz); blk -= VAS_EBOOT_UFS_DIRBLKS; log_indirsz = data->log2_blksz - LOG_INODE_BLKSZ; indirsz = 1 << log_indirsz; /* Single indirect block. */ if (blk < indirsz) { VasEBoot_ufs_blk_t indir; VasEBoot_disk_read (data->disk, ((VasEBoot_disk_addr_t) INODE_INDIRBLOCKS (data, 0)) << log2_blksz, blk * sizeof (indir), sizeof (indir), &indir); return indir; } blk -= indirsz; /* Double indirect block. */ if (blk < (VasEBoot_disk_addr_t) indirsz * (VasEBoot_disk_addr_t) indirsz) { VasEBoot_ufs_blk_t indir; VasEBoot_disk_read (data->disk, ((VasEBoot_disk_addr_t) INODE_INDIRBLOCKS (data, 1)) << log2_blksz, (blk >> log_indirsz) * sizeof (indir), sizeof (indir), &indir); VasEBoot_disk_read (data->disk, VasEBoot_ufs_to_cpu_blk (indir) << log2_blksz, (blk & ((1 << log_indirsz) - 1)) * sizeof (indir), sizeof (indir), &indir); return indir; } blk -= (VasEBoot_disk_addr_t) indirsz * (VasEBoot_disk_addr_t) indirsz; /* Triple indirect block. */ if (!(blk >> (3 * log_indirsz))) { VasEBoot_ufs_blk_t indir; VasEBoot_disk_read (data->disk, ((VasEBoot_disk_addr_t) INODE_INDIRBLOCKS (data, 2)) << log2_blksz, (blk >> (2 * log_indirsz)) * sizeof (indir), sizeof (indir), &indir); VasEBoot_disk_read (data->disk, VasEBoot_ufs_to_cpu_blk (indir) << log2_blksz, ((blk >> log_indirsz) & ((1 << log_indirsz) - 1)) * sizeof (indir), sizeof (indir), &indir); VasEBoot_disk_read (data->disk, VasEBoot_ufs_to_cpu_blk (indir) << log2_blksz, (blk & ((1 << log_indirsz) - 1)) * sizeof (indir), sizeof (indir), &indir); return indir; } VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "ufs does not support quadruple indirect blocks"); return 0; } /* Read LEN bytes from the file described by DATA starting with byte POS. Return the amount of read bytes in READ. */ static VasEBoot_ssize_t VasEBoot_ufs_read_file (struct VasEBoot_ufs_data *data, VasEBoot_disk_read_hook_t read_hook, void *read_hook_data, VasEBoot_off_t pos, VasEBoot_size_t len, char *buf) { struct VasEBoot_ufs_sblock *sblock = &data->sblock; VasEBoot_off_t i; VasEBoot_off_t blockcnt; /* Adjust len so it we can't read past the end of the file. */ if (len + pos > INODE_SIZE (data)) len = INODE_SIZE (data) - pos; blockcnt = (len + pos + UFS_BLKSZ (sblock) - 1) >> UFS_LOG_BLKSZ (sblock); for (i = pos >> UFS_LOG_BLKSZ (sblock); i < blockcnt; i++) { VasEBoot_disk_addr_t blknr; VasEBoot_off_t blockoff; VasEBoot_off_t blockend = UFS_BLKSZ (sblock); int skipfirst = 0; blockoff = pos & (UFS_BLKSZ (sblock) - 1); blknr = VasEBoot_ufs_get_file_block (data, i); if (VasEBoot_errno) return -1; /* Last block. */ if (i == blockcnt - 1) { blockend = (len + pos) & (UFS_BLKSZ (sblock) - 1); if (!blockend) blockend = UFS_BLKSZ (sblock); } /* First block. */ if (i == (pos >> UFS_LOG_BLKSZ (sblock))) { skipfirst = blockoff; blockend -= skipfirst; } /* XXX: If the block number is 0 this block is not stored on disk but is zero filled instead. */ if (blknr) { data->disk->read_hook = read_hook; data->disk->read_hook_data = read_hook_data; VasEBoot_disk_read (data->disk, blknr << VasEBoot_ufs_to_cpu32 (data->sblock.log2_blksz), skipfirst, blockend, buf); data->disk->read_hook = 0; if (VasEBoot_errno) return -1; } else VasEBoot_memset (buf, 0, blockend); buf += UFS_BLKSZ (sblock) - skipfirst; } return len; } /* Read inode INO from the mounted filesystem described by DATA. This inode is used by default now. */ static VasEBoot_err_t VasEBoot_ufs_read_inode (struct VasEBoot_ufs_data *data, int ino, char *inode) { struct VasEBoot_ufs_sblock *sblock = &data->sblock; /* Determine the group the inode is in. */ int group = ino / VasEBoot_ufs_to_cpu32 (sblock->ino_per_group); /* Determine the inode within the group. */ int grpino = ino % VasEBoot_ufs_to_cpu32 (sblock->ino_per_group); /* The first block of the group. */ int grpblk = group * (VasEBoot_ufs_to_cpu32 (sblock->frags_per_group)); #ifndef MODE_UFS2 grpblk += VasEBoot_ufs_to_cpu32 (sblock->cylg_offset) * (group & (~VasEBoot_ufs_to_cpu32 (sblock->cylg_mask))); #endif if (!inode) { inode = (char *) &data->inode; data->ino = ino; } VasEBoot_disk_read (data->disk, ((VasEBoot_ufs_to_cpu32 (sblock->inoblk_offs) + grpblk) << VasEBoot_ufs_to_cpu32 (data->sblock.log2_blksz)) + grpino / UFS_INODE_PER_BLOCK, (grpino % UFS_INODE_PER_BLOCK) * sizeof (struct VasEBoot_ufs_inode), sizeof (struct VasEBoot_ufs_inode), inode); return VasEBoot_errno; } /* Lookup the symlink the current inode points to. INO is the inode number of the directory the symlink is relative to. */ static VasEBoot_err_t VasEBoot_ufs_lookup_symlink (struct VasEBoot_ufs_data *data, int ino) { char *symlink; VasEBoot_size_t sz = INODE_SIZE (data); if (++data->linknest > VAS_EBOOT_UFS_MAX_SYMLNK_CNT) return VasEBoot_error (VAS_EBOOT_ERR_SYMLINK_LOOP, N_("too deep nesting of symlinks")); symlink = VasEBoot_malloc (sz + 1); if (!symlink) return VasEBoot_errno; /* Normally we should just check that data->inode.nblocks == 0. However old Linux doesn't maintain nblocks correctly and so it's always 0. If size is bigger than inline space then the symlink is surely not inline. */ /* Check against zero is paylindromic, no need to swap. */ if (data->inode.nblocks == 0 && INODE_SIZE (data) <= sizeof (data->inode.symlink)) VasEBoot_strlcpy (symlink, (char *) data->inode.symlink, sz); else { if (VasEBoot_ufs_read_file (data, 0, 0, 0, sz, symlink) < 0) { VasEBoot_free(symlink); return VasEBoot_errno; } } symlink[sz] = '\0'; /* The symlink is an absolute path, go back to the root inode. */ if (symlink[0] == '/') ino = VAS_EBOOT_UFS_INODE; /* Now load in the old inode. */ if (VasEBoot_ufs_read_inode (data, ino, 0)) { VasEBoot_free (symlink); return VasEBoot_errno; } VasEBoot_ufs_find_file (data, symlink); VasEBoot_free (symlink); return VasEBoot_errno; } /* Find the file with the pathname PATH on the filesystem described by DATA. */ static VasEBoot_err_t VasEBoot_ufs_find_file (struct VasEBoot_ufs_data *data, const char *path) { const char *name; const char *next = path; unsigned int pos = 0; int dirino; char *filename; /* We reject filenames longer than the one we're looking for without reading, so this allocation is enough. */ filename = VasEBoot_malloc (VasEBoot_strlen (path) + 2); if (!filename) return VasEBoot_errno; while (1) { struct VasEBoot_ufs_dirent dirent; name = next; /* Skip the first slash. */ while (*name == '/') name++; if (*name == 0) { VasEBoot_free (filename); return VAS_EBOOT_ERR_NONE; } if ((INODE_MODE(data) & VAS_EBOOT_UFS_ATTR_TYPE) != VAS_EBOOT_UFS_ATTR_DIR) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILE_TYPE, N_("not a directory")); goto fail; } /* Extract the actual part from the pathname. */ for (next = name; *next && *next != '/'; next++); for (pos = 0; ; pos += VasEBoot_ufs_to_cpu16 (dirent.direntlen)) { int namelen; if (pos >= INODE_SIZE (data)) { VasEBoot_error (VAS_EBOOT_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path); goto fail; } if (VasEBoot_ufs_read_file (data, 0, 0, pos, sizeof (dirent), (char *) &dirent) < 0) goto fail; #ifdef MODE_UFS2 namelen = dirent.namelen_bsd; #else namelen = VasEBoot_ufs_to_cpu16 (dirent.namelen); #endif if (namelen < next - name) continue; if (VasEBoot_ufs_read_file (data, 0, 0, pos + sizeof (dirent), next - name + (namelen != next - name), filename) < 0) goto fail; if (VasEBoot_strncmp (name, filename, next - name) == 0 && (namelen == next - name || filename[next - name] == '\0')) { dirino = data->ino; VasEBoot_ufs_read_inode (data, VasEBoot_ufs_to_cpu32 (dirent.ino), 0); if ((INODE_MODE(data) & VAS_EBOOT_UFS_ATTR_TYPE) == VAS_EBOOT_UFS_ATTR_LNK) { VasEBoot_ufs_lookup_symlink (data, dirino); if (VasEBoot_errno) goto fail; } break; } } } fail: VasEBoot_free (filename); return VasEBoot_errno; } /* Mount the filesystem on the disk DISK. */ static struct VasEBoot_ufs_data * VasEBoot_ufs_mount (VasEBoot_disk_t disk) { struct VasEBoot_ufs_data *data; int *sblklist = sblocklist; data = VasEBoot_malloc (sizeof (struct VasEBoot_ufs_data)); if (!data) return 0; /* Find a UFS sblock. */ while (*sblklist != -1) { VasEBoot_disk_read (disk, *sblklist, 0, sizeof (struct VasEBoot_ufs_sblock), &data->sblock); if (VasEBoot_errno) goto fail; /* No need to byteswap bsize in this check. It works the same on both endiannesses. */ if (data->sblock.magic == VasEBoot_cpu_to_ufs32_compile_time (VAS_EBOOT_UFS_MAGIC) && data->sblock.bsize != 0 && ((data->sblock.bsize & (data->sblock.bsize - 1)) == 0) && data->sblock.ino_per_group != 0) { for (data->log2_blksz = 0; (1U << data->log2_blksz) < VasEBoot_ufs_to_cpu32 (data->sblock.bsize); data->log2_blksz++); data->disk = disk; data->linknest = 0; return data; } sblklist++; } fail: if (VasEBoot_errno == VAS_EBOOT_ERR_NONE || VasEBoot_errno == VAS_EBOOT_ERR_OUT_OF_RANGE) { #ifdef MODE_UFS2 VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "not an ufs2 filesystem"); #else VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "not an ufs1 filesystem"); #endif } VasEBoot_free (data); return 0; } static VasEBoot_err_t VasEBoot_ufs_dir (VasEBoot_device_t device, const char *path, VasEBoot_fs_dir_hook_t hook, void *hook_data) { struct VasEBoot_ufs_data *data; unsigned int pos = 0; data = VasEBoot_ufs_mount (device->disk); if (!data) return VasEBoot_errno; VasEBoot_ufs_read_inode (data, VAS_EBOOT_UFS_INODE, 0); if (VasEBoot_errno) return VasEBoot_errno; if (!path || path[0] != '/') { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path); return VasEBoot_errno; } VasEBoot_ufs_find_file (data, path); if (VasEBoot_errno) goto fail; if ((INODE_MODE (data) & VAS_EBOOT_UFS_ATTR_TYPE) != VAS_EBOOT_UFS_ATTR_DIR) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILE_TYPE, N_("not a directory")); goto fail; } while (pos < INODE_SIZE (data)) { struct VasEBoot_ufs_dirent dirent; int namelen; if (VasEBoot_ufs_read_file (data, 0, 0, pos, sizeof (dirent), (char *) &dirent) < 0) break; if (dirent.direntlen == 0) break; #ifdef MODE_UFS2 namelen = dirent.namelen_bsd; #else namelen = VasEBoot_ufs_to_cpu16 (dirent.namelen); #endif char *filename = VasEBoot_malloc (namelen + 1); if (!filename) goto fail; struct VasEBoot_dirhook_info info; struct VasEBoot_ufs_inode inode; VasEBoot_memset (&info, 0, sizeof (info)); if (VasEBoot_ufs_read_file (data, 0, 0, pos + sizeof (dirent), namelen, filename) < 0) { VasEBoot_free (filename); break; } filename[namelen] = '\0'; VasEBoot_ufs_read_inode (data, VasEBoot_ufs_to_cpu32 (dirent.ino), (char *) &inode); info.dir = ((VasEBoot_ufs_to_cpu16 (inode.mode) & VAS_EBOOT_UFS_ATTR_TYPE) == VAS_EBOOT_UFS_ATTR_DIR); #ifdef MODE_UFS2 info.mtime = VasEBoot_ufs_to_cpu64 (inode.mtime); #else info.mtime = VasEBoot_ufs_to_cpu32 (inode.mtime); #endif info.mtimeset = 1; if (hook (filename, &info, hook_data)) { VasEBoot_free (filename); break; } VasEBoot_free (filename); pos += VasEBoot_ufs_to_cpu16 (dirent.direntlen); } fail: VasEBoot_free (data); return VasEBoot_errno; } /* Open a file named NAME and initialize FILE. */ static VasEBoot_err_t VasEBoot_ufs_open (struct VasEBoot_file *file, const char *name) { struct VasEBoot_ufs_data *data; data = VasEBoot_ufs_mount (file->device->disk); if (!data) return VasEBoot_errno; VasEBoot_ufs_read_inode (data, 2, 0); if (VasEBoot_errno) { VasEBoot_free (data); return VasEBoot_errno; } if (!name || name[0] != '/') { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILENAME, N_("invalid file name `%s'"), name); return VasEBoot_errno; } VasEBoot_ufs_find_file (data, name); if (VasEBoot_errno) { VasEBoot_free (data); return VasEBoot_errno; } file->data = data; file->size = INODE_SIZE (data); return VAS_EBOOT_ERR_NONE; } static VasEBoot_ssize_t VasEBoot_ufs_read (VasEBoot_file_t file, char *buf, VasEBoot_size_t len) { struct VasEBoot_ufs_data *data = (struct VasEBoot_ufs_data *) file->data; return VasEBoot_ufs_read_file (data, file->read_hook, file->read_hook_data, file->offset, len, buf); } static VasEBoot_err_t VasEBoot_ufs_close (VasEBoot_file_t file) { VasEBoot_free (file->data); return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_ufs_label (VasEBoot_device_t device, char **label) { struct VasEBoot_ufs_data *data = 0; VasEBoot_dl_ref (my_mod); *label = 0; data = VasEBoot_ufs_mount (device->disk); if (data) *label = VasEBoot_strdup ((char *) data->sblock.volume_name); VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } static VasEBoot_err_t VasEBoot_ufs_uuid (VasEBoot_device_t device, char **uuid) { struct VasEBoot_ufs_data *data; VasEBoot_disk_t disk = device->disk; VasEBoot_dl_ref (my_mod); data = VasEBoot_ufs_mount (disk); if (data && (data->sblock.uuidhi != 0 || data->sblock.uuidlow != 0)) *uuid = VasEBoot_xasprintf ("%08x%08x", (unsigned) VasEBoot_ufs_to_cpu32 (data->sblock.uuidhi), (unsigned) VasEBoot_ufs_to_cpu32 (data->sblock.uuidlow)); else *uuid = NULL; VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } /* Get mtime. */ static VasEBoot_err_t VasEBoot_ufs_mtime (VasEBoot_device_t device, VasEBoot_int64_t *tm) { struct VasEBoot_ufs_data *data = 0; VasEBoot_dl_ref (my_mod); data = VasEBoot_ufs_mount (device->disk); if (!data) *tm = 0; else { *tm = VasEBoot_ufs_to_cpu32 (data->sblock.mtime); #ifdef MODE_UFS2 if (*tm < (VasEBoot_int64_t) VasEBoot_ufs_to_cpu64 (data->sblock.mtime2)) *tm = VasEBoot_ufs_to_cpu64 (data->sblock.mtime2); #endif } VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } static struct VasEBoot_fs VasEBoot_ufs_fs = { #ifdef MODE_UFS2 .name = "ufs2", #else #ifdef MODE_BIGENDIAN .name = "ufs1_be", #else .name = "ufs1", #endif #endif .fs_dir = VasEBoot_ufs_dir, .fs_open = VasEBoot_ufs_open, .fs_read = VasEBoot_ufs_read, .fs_close = VasEBoot_ufs_close, .fs_label = VasEBoot_ufs_label, .fs_uuid = VasEBoot_ufs_uuid, .fs_mtime = VasEBoot_ufs_mtime, /* FIXME: set reserved_first_sector. */ #ifdef VAS_EBOOT_UTIL .blocklist_install = 1, #endif .next = 0 }; #ifdef MODE_UFS2 VAS_EBOOT_MOD_INIT(ufs2) #else #ifdef MODE_BIGENDIAN VAS_EBOOT_MOD_INIT(ufs1_be) #else VAS_EBOOT_MOD_INIT(ufs1) #endif #endif { if (!VasEBoot_is_lockdown ()) { VasEBoot_ufs_fs.mod = mod; VasEBoot_fs_register (&VasEBoot_ufs_fs); } my_mod = mod; } #ifdef MODE_UFS2 VAS_EBOOT_MOD_FINI(ufs2) #else #ifdef MODE_BIGENDIAN VAS_EBOOT_MOD_FINI(ufs1_be) #else VAS_EBOOT_MOD_FINI(ufs1) #endif #endif { if (!VasEBoot_is_lockdown ()) VasEBoot_fs_unregister (&VasEBoot_ufs_fs); }