From: Raphael S. Carvalho <raphael.scarv at gmail.com> Change since v1: * Fix bug on dentry structure (thank you specification; btw, sarcasm), and consequently a bug on ufs_readdir. * Add readlink support (applied tests for symlinks whose destionation path were stored in blk pointers and the file itself). * Several improvements. Wrote the documentation below. I think it would be good to push the doc to the wiki as soon as the UFS support gets merged. Unix Fast File System (UFS/FFS) 1/2 on Syslinux - (usage/install) ----- There is a confusion about the name of this file system, then I decided to contact the author, Kirk McKusick, who replied: "The name has always been confusing (my bad). The code is broken into two parts, the part that handles naming (UFS where the U stands for Unix), and the part that handles disk layout (FFS where the F stands for Fast). When the two parts are put together they are called UFS/FFS or more commonly just UFS." Dependencies: - Environment: Require a *GNU/Linux* system to follow these steps. - Package: ufsutils (UFS filesystems utilities) - Device driver: UFS support on Linux: you need a kernel with the option CONFIG_UFS_FS_WRITE enabled or at least configured as a module. For the latter case, load the module ufs.ko with modprobe (probably located at: /lib/modules/). If this option wasn't even configured as a module, then you need to recompile your kernel with that option enabled. Further information can be found in the link below: http://oz123.github.io/writings/2012-11-26-enable-ufs-write-support-debian/ Creating an UFS image: sudo dd if=/dev/zero of=<image> bs= count=; - UFS1: sudo mkfs.ufs -O 1 <image>; - UFS2: sudo mkfs.ufs <image>; Mounting an UFS image: - UFS1: sudo mount -t ufs -o rw,loop,ufstype=44bsd <image> <mountpoint>; - UFS2: sudo mount -t ufs -o rw,loop,ufstype=ufs2 <image> <mountpoint>; Installing syslinux on an UFS image: - Install extlinux into your mount point: sudo extlinux -i <mountpoint>; - Finally, set up the syslinux config file. Thanks for pcacjr who introduced me to Syslinux :-) Raphael S. Carvalho (2): installer: Add UFS1/2 support for Extlinux installer core/fs: Add support to Unix File system 1/2. core/fs/ufs/bmap.c | 202 ++++++++++++++++++++ core/fs/ufs/ufs.c | 486 ++++++++++++++++++++++++++++++++++++++++++++++++ core/fs/ufs/ufs.h | 254 +++++++++++++++++++++++++ core/ldlinux.asm | 2 + extlinux/main.c | 51 ++++- extlinux/ufs.h | 26 +++ extlinux/ufs_fs.h | 307 ++++++++++++++++++++++++++++++ libinstaller/syslxfs.h | 4 +- 8 files changed, 1322 insertions(+), 10 deletions(-) create mode 100644 core/fs/ufs/bmap.c create mode 100644 core/fs/ufs/ufs.c create mode 100644 core/fs/ufs/ufs.h create mode 100644 extlinux/ufs.h create mode 100644 extlinux/ufs_fs.h -- 1.7.2.5
Raphael S.Carvalho
2014-May-29 23:35 UTC
[syslinux] [PATCH v2 1/2] installer: Add UFS1/2 support for Extlinux installer
From: Raphael S. Carvalho <raphael.scarv at gmail.com> It's needed to enumerate both UFS1 and UFS2 as they have different magic numbers and super block offsets. Neither UFS1 nor UFS2 have COW feature, therefore, ldlinux.sys can be installed as a regular file. Signed-off-by: Raphael S. Carvalho <raphael.scarv at gmail.com> --- extlinux/main.c | 51 +++++++-- extlinux/ufs.h | 26 ++++ extlinux/ufs_fs.h | 307 ++++++++++++++++++++++++++++++++++++++++++++++++ libinstaller/syslxfs.h | 4 +- 4 files changed, 378 insertions(+), 10 deletions(-) create mode 100644 extlinux/ufs.h create mode 100644 extlinux/ufs_fs.h diff --git a/extlinux/main.c b/extlinux/main.c index 47c1e68..6b9a04b 100644 --- a/extlinux/main.c +++ b/extlinux/main.c @@ -14,8 +14,8 @@ /* * extlinux.c * - * Install the syslinux boot block on an fat, ntfs, ext2/3/4, btrfs and xfs - * filesystem. + * Install the syslinux boot block on an fat, ntfs, ext2/3/4, btrfs, xfs, + * and ufs1/2 filesystem. */ #define _GNU_SOURCE /* Enable everything */ @@ -50,6 +50,8 @@ #include "xfs.h" #include "xfs_types.h" #include "xfs_sb.h" +#include "ufs.h" +#include "ufs_fs.h" #include "misc.h" #include "version.h" #include "syslxint.h" @@ -308,7 +310,7 @@ static int patch_file_and_bootblock(int fd, const char *dir, int devfd) nsect += 2; /* Two sectors for the ADV */ sectp = alloca(sizeof(sector_t) * nsect); if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS || - fs_type == XFS) { + fs_type == XFS || fs_type == UFS1 || fs_type == UFS2) { if (sectmap(fd, sectp, nsect)) { perror("bmap"); exit(1); @@ -342,6 +344,7 @@ int install_bootblock(int fd, const char *device) struct fat_boot_sector sb3; struct ntfs_boot_sector sb4; xfs_sb_t sb5; + struct ufs_super_block sb6; bool ok = false; if (fs_type == EXT2) { @@ -395,11 +398,25 @@ int install_bootblock(int fd, const char *device) ok = true; } + } else if (fs_type == UFS1 || fs_type == UFS2) { + uint32_t sblock_off = (fs_type == UFS1) ? + SBLOCK_UFS1 : SBLOCK_UFS2; + uint32_t ufs_smagic = (fs_type == UFS1) ? + UFS1_SUPER_MAGIC : UFS2_SUPER_MAGIC; + + if (xpread(fd, &sb6, sizeof sb6, sblock_off) != sizeof sb6) { + perror("reading superblock"); + return 1; + } + + if (sb6.fs_magic == ufs_smagic) + ok = true; } if (!ok) { fprintf(stderr, - "no fat, ntfs, ext2/3/4, btrfs or xfs superblock found on %s\n", + "no fat, ntfs, ext2/3/4, btrfs, xfs " + "or ufs1/2 superblock found on %s\n", device); return 1; } @@ -959,7 +976,8 @@ static char * get_default_subvol(char * rootdir, char * subvol) static int install_file(const char *path, int devfd, struct stat *rst) { - if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) + if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS + || fs_type == UFS1 || fs_type == UFS2) return ext2_fat_install_file(path, devfd, rst); else if (fs_type == BTRFS) return btrfs_install_file(path, devfd, rst); @@ -987,7 +1005,7 @@ static int validate_device(const char *path, int devfd) struct statfs sfs; int pfd; int rv = -1; - + pfd = open(path, O_RDONLY|O_DIRECTORY); if (pfd < 0) goto err; @@ -1067,6 +1085,16 @@ static const char *find_device(const char *mtab_file, dev_t dev) done = true; break; } + + break; + case UFS1: + case UFS2: + if (!strcmp(mnt->mnt_type, "ufs") && !stat(mnt->mnt_fsname, &dst) && + dst.st_rdev == dev) { + done = true; + } + + break; case NONE: break; } @@ -1191,7 +1219,7 @@ static int validate_device_btrfs(int pfd, int dfd) return -1; return 0; /* It's good! */ -} +} static const char *find_device_btrfs(const char *path) { @@ -1280,7 +1308,7 @@ static const char *get_devname(const char *path) fprintf(stderr, "%s: cannot create device %s\n", program, devname); return devname; } - + atexit(device_cleanup); /* unlink the device node on exit */ devname = devname_buf; } @@ -1334,10 +1362,15 @@ static int open_device(const char *path, struct stat *st, const char **_devname) fs_type = NTFS; else if (sfs.f_type == XFS_SUPER_MAGIC) fs_type = XFS; + else if (sfs.f_type == UFS1_SUPER_MAGIC) + fs_type = UFS1; + else if (sfs.f_type == UFS2_SUPER_MAGIC) + fs_type = UFS2; if (!fs_type) { fprintf(stderr, - "%s: not a fat, ntfs, ext2/3/4, btrfs or xfs filesystem: %s\n", + "%s: not a fat, ntfs, ext2/3/4, btrfs, xfs or" + "ufs1/2 filesystem: %s\n", program, path); return -1; } diff --git a/extlinux/ufs.h b/extlinux/ufs.h new file mode 100644 index 0000000..d324699 --- /dev/null +++ b/extlinux/ufs.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com> + * + * 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. +*/ + +#ifndef UFS_H_ +#define UFS_H_ + +#define UFS1_SUPER_MAGIC 0x00011954 +#define UFS2_SUPER_MAGIC 0x19540119 + +#endif /* UFS_H_ */ diff --git a/extlinux/ufs_fs.h b/extlinux/ufs_fs.h new file mode 100644 index 0000000..ff8a4e7 --- /dev/null +++ b/extlinux/ufs_fs.h @@ -0,0 +1,307 @@ +/* + * Taken from Linux kernel tree (linux/fs/ufs) + * linux/include/linux/ufs_fs.h + * + * Copyright (C) 1996 + * Adrian Rodriguez (adrian at franklins-tower.rutgers.edu) + * Laboratory for Computer Science Research Computing Facility + * Rutgers, The State University of New Jersey + * + * Copyright (c) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com> + * + * Clean swab support by Fare <fare at tunes.org> + * just hope no one is using NNUUXXI on __?64 structure elements + * 64-bit clean thanks to Maciej W. Rozycki <macro at ds2.pg.gda.pl> + * + * 4.4BSD (FreeBSD) support added on February 1st 1998 by + * Niels Kristian Bech Jensen <nkbj at image.dk> partially based + * on code by Martin von Loewis <martin at mira.isdn.cs.tu-berlin.de>. + * + * NeXTstep support added on February 5th 1998 by + * Niels Kristian Bech Jensen <nkbj at image.dk>. + * + * Write support by Daniel Pirkl <daniel.pirkl at email.cz> + * + * HP/UX hfs filesystem support added by + * Martin K. Petersen <mkp at mkp.net>, August 1999 + * + * UFS2 (of FreeBSD 5.x) support added by + * Niraj Kumar <niraj17 at iitbombay.org> , Jan 2004 + * + */ + +#ifndef __LINUX_UFS_FS_H +#define __LINUX_UFS_FS_H + +#include <inttypes.h> + +typedef uint64_t __fs64; +typedef uint32_t __fs32; +typedef uint16_t __fs16; + +#define UFS_BBLOCK 0 +#define UFS_BBSIZE 8192 +#define UFS_SBLOCK 8192 +#define UFS_SBSIZE 8192 + +#define UFS_SECTOR_SIZE 512 +#define UFS_SECTOR_BITS 9 +#define UFS_MAGIC 0x00011954 +#define UFS_MAGIC_BW 0x0f242697 +#define UFS2_MAGIC 0x19540119 +#define UFS_CIGAM 0x54190100 /* byteswapped MAGIC */ + +/* Copied from FreeBSD */ +/* + * Each disk drive contains some number of filesystems. + * A filesystem consists of a number of cylinder groups. + * Each cylinder group has inodes and data. + * + * A filesystem is described by its super-block, which in turn + * describes the cylinder groups. The super-block is critical + * data and is replicated in each cylinder group to protect against + * catastrophic loss. This is done at `newfs' time and the critical + * super-block data does not change, so the copies need not be + * referenced further unless disaster strikes. + * + * For filesystem fs, the offsets of the various blocks of interest + * are given in the super block as: + * [fs->fs_sblkno] Super-block + * [fs->fs_cblkno] Cylinder group block + * [fs->fs_iblkno] Inode blocks + * [fs->fs_dblkno] Data blocks + * The beginning of cylinder group cg in fs, is given by + * the ``cgbase(fs, cg)'' macro. + * + * Depending on the architecture and the media, the superblock may + * reside in any one of four places. For tiny media where every block + * counts, it is placed at the very front of the partition. Historically, + * UFS1 placed it 8K from the front to leave room for the disk label and + * a small bootstrap. For UFS2 it got moved to 64K from the front to leave + * room for the disk label and a bigger bootstrap, and for really piggy + * systems we check at 256K from the front if the first three fail. In + * all cases the size of the superblock will be SBLOCKSIZE. All values are + * given in byte-offset form, so they do not imply a sector size. The + * SBLOCKSEARCH specifies the order in which the locations should be searched. + */ +#define SBLOCK_FLOPPY 0 +#define SBLOCK_UFS1 8192 +#define SBLOCK_UFS2 65536 +#define SBLOCK_PIGGY 262144 +#define SBLOCKSIZE 8192 +#define SBLOCKSEARCH \ + { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 } + +#define UFS_MAXNAMLEN 255 +#define UFS_MAXMNTLEN 512 +#define UFS2_MAXMNTLEN 468 +#define UFS2_MAXVOLLEN 32 +#define UFS_MAXCSBUFS 31 +#define UFS_LINK_MAX 32000 +/* +#define UFS2_NOCSPTRS ((128 / sizeof(void *)) - 4) +*/ +#define UFS2_NOCSPTRS 28 + +/* + * UFS_DIR_PAD defines the directory entries boundaries + * (must be a multiple of 4) + */ +#define UFS_DIR_PAD 4 +#define UFS_DIR_ROUND (UFS_DIR_PAD - 1) +#define UFS_DIR_REC_LEN(name_len) (((name_len) + 1 + 8 + UFS_DIR_ROUND) & ~UFS_DIR_ROUND) + +struct ufs_timeval { + __fs32 tv_sec; + __fs32 tv_usec; +}; + +struct ufs_dir_entry { + __fs32 d_ino; /* inode number of this entry */ + __fs16 d_reclen; /* length of this entry */ + union { + __fs16 d_namlen; /* actual length of d_name */ + struct { + __u8 d_type; /* file type */ + __u8 d_namlen; /* length of string in d_name */ + } d_44; + } d_u; + __u8 d_name[UFS_MAXNAMLEN + 1]; /* file name */ +}; + +struct ufs_csum { + __fs32 cs_ndir; /* number of directories */ + __fs32 cs_nbfree; /* number of free blocks */ + __fs32 cs_nifree; /* number of free inodes */ + __fs32 cs_nffree; /* number of free frags */ +}; +struct ufs2_csum_total { + __fs64 cs_ndir; /* number of directories */ + __fs64 cs_nbfree; /* number of free blocks */ + __fs64 cs_nifree; /* number of free inodes */ + __fs64 cs_nffree; /* number of free frags */ + __fs64 cs_numclusters; /* number of free clusters */ + __fs64 cs_spare[3]; /* future expansion */ +}; + +struct ufs_csum_core { + __u64 cs_ndir; /* number of directories */ + __u64 cs_nbfree; /* number of free blocks */ + __u64 cs_nifree; /* number of free inodes */ + __u64 cs_nffree; /* number of free frags */ + __u64 cs_numclusters; /* number of free clusters */ +}; + +struct ufs_super_block { + union { + struct { + __fs32 fs_link; /* UNUSED */ + } fs_42; + struct { + __fs32 fs_state; /* file system state flag */ + } fs_sun; + } fs_u0; + __fs32 fs_rlink; /* UNUSED */ + __fs32 fs_sblkno; /* addr of super-block in filesys */ + __fs32 fs_cblkno; /* offset of cyl-block in filesys */ + __fs32 fs_iblkno; /* offset of inode-blocks in filesys */ + __fs32 fs_dblkno; /* offset of first data after cg */ + __fs32 fs_cgoffset; /* cylinder group offset in cylinder */ + __fs32 fs_cgmask; /* used to calc mod fs_ntrak */ + __fs32 fs_time; /* last time written -- time_t */ + __fs32 fs_size; /* number of blocks in fs */ + __fs32 fs_dsize; /* number of data blocks in fs */ + __fs32 fs_ncg; /* number of cylinder groups */ + __fs32 fs_bsize; /* size of basic blocks in fs */ + __fs32 fs_fsize; /* size of frag blocks in fs */ + __fs32 fs_frag; /* number of frags in a block in fs */ +/* these are configuration parameters */ + __fs32 fs_minfree; /* minimum percentage of free blocks */ + __fs32 fs_rotdelay; /* num of ms for optimal next block */ + __fs32 fs_rps; /* disk revolutions per second */ +/* these fields can be computed from the others */ + __fs32 fs_bmask; /* ``blkoff'' calc of blk offsets */ + __fs32 fs_fmask; /* ``fragoff'' calc of frag offsets */ + __fs32 fs_bshift; /* ``lblkno'' calc of logical blkno */ + __fs32 fs_fshift; /* ``numfrags'' calc number of frags */ +/* these are configuration parameters */ + __fs32 fs_maxcontig; /* max number of contiguous blks */ + __fs32 fs_maxbpg; /* max number of blks per cyl group */ +/* these fields can be computed from the others */ + __fs32 fs_fragshift; /* block to frag shift */ + __fs32 fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ + __fs32 fs_sbsize; /* actual size of super block */ + __fs32 fs_csmask; /* csum block offset */ + __fs32 fs_csshift; /* csum block number */ + __fs32 fs_nindir; /* value of NINDIR */ + __fs32 fs_inopb; /* value of INOPB */ + __fs32 fs_nspf; /* value of NSPF */ +/* yet another configuration parameter */ + __fs32 fs_optim; /* optimization preference, see below */ +/* these fields are derived from the hardware */ + union { + struct { + __fs32 fs_npsect; /* # sectors/track including spares */ + } fs_sun; + struct { + __fs32 fs_state; /* file system state time stamp */ + } fs_sunx86; + } fs_u1; + __fs32 fs_interleave; /* hardware sector interleave */ + __fs32 fs_trackskew; /* sector 0 skew, per track */ +/* a unique id for this filesystem (currently unused and unmaintained) */ +/* In 4.3 Tahoe this space is used by fs_headswitch and fs_trkseek */ +/* Neither of those fields is used in the Tahoe code right now but */ +/* there could be problems if they are. */ + __fs32 fs_id[2]; /* file system id */ +/* sizes determined by number of cylinder groups and their sizes */ + __fs32 fs_csaddr; /* blk addr of cyl grp summary area */ + __fs32 fs_cssize; /* size of cyl grp summary area */ + __fs32 fs_cgsize; /* cylinder group size */ +/* these fields are derived from the hardware */ + __fs32 fs_ntrak; /* tracks per cylinder */ + __fs32 fs_nsect; /* sectors per track */ + __fs32 fs_spc; /* sectors per cylinder */ +/* this comes from the disk driver partitioning */ + __fs32 fs_ncyl; /* cylinders in file system */ +/* these fields can be computed from the others */ + __fs32 fs_cpg; /* cylinders per group */ + __fs32 fs_ipg; /* inodes per cylinder group */ + __fs32 fs_fpg; /* blocks per group * fs_frag */ +/* this data must be re-computed after crashes */ + struct ufs_csum fs_cstotal; /* cylinder summary information */ +/* these fields are cleared at mount time */ + __s8 fs_fmod; /* super block modified flag */ + __s8 fs_clean; /* file system is clean flag */ + __s8 fs_ronly; /* mounted read-only flag */ + __s8 fs_flags; + union { + struct { + __s8 fs_fsmnt[UFS_MAXMNTLEN];/* name mounted on */ + __fs32 fs_cgrotor; /* last cg searched */ + __fs32 fs_csp[UFS_MAXCSBUFS];/*list of fs_cs info buffers */ + __fs32 fs_maxcluster; + __fs32 fs_cpc; /* cyl per cycle in postbl */ + __fs16 fs_opostbl[16][8]; /* old rotation block list head */ + } fs_u1; + struct { + __s8 fs_fsmnt[UFS2_MAXMNTLEN]; /* name mounted on */ + __u8 fs_volname[UFS2_MAXVOLLEN]; /* volume name */ + __fs64 fs_swuid; /* system-wide uid */ + __fs32 fs_pad; /* due to alignment of fs_swuid */ + __fs32 fs_cgrotor; /* last cg searched */ + __fs32 fs_ocsp[UFS2_NOCSPTRS]; /*list of fs_cs info buffers */ + __fs32 fs_contigdirs;/*# of contiguously allocated dirs */ + __fs32 fs_csp; /* cg summary info buffer for fs_cs */ + __fs32 fs_maxcluster; + __fs32 fs_active;/* used by snapshots to track fs */ + __fs32 fs_old_cpc; /* cyl per cycle in postbl */ + __fs32 fs_maxbsize;/*maximum blocking factor permitted */ + __fs64 fs_sparecon64[17];/*old rotation block list head */ + __fs64 fs_sblockloc; /* byte offset of standard superblock */ + struct ufs2_csum_total fs_cstotal;/*cylinder summary information*/ + struct ufs_timeval fs_time; /* last time written */ + __fs64 fs_size; /* number of blocks in fs */ + __fs64 fs_dsize; /* number of data blocks in fs */ + __fs64 fs_csaddr; /* blk addr of cyl grp summary area */ + __fs64 fs_pendingblocks;/* blocks in process of being freed */ + __fs32 fs_pendinginodes;/*inodes in process of being freed */ + } fs_u2; + } fs_u11; + union { + struct { + __fs32 fs_sparecon[53];/* reserved for future constants */ + __fs32 fs_reclaim; + __fs32 fs_sparecon2[1]; + __fs32 fs_state; /* file system state time stamp */ + __fs32 fs_qbmask[2]; /* ~usb_bmask */ + __fs32 fs_qfmask[2]; /* ~usb_fmask */ + } fs_sun; + struct { + __fs32 fs_sparecon[53];/* reserved for future constants */ + __fs32 fs_reclaim; + __fs32 fs_sparecon2[1]; + __fs32 fs_npsect; /* # sectors/track including spares */ + __fs32 fs_qbmask[2]; /* ~usb_bmask */ + __fs32 fs_qfmask[2]; /* ~usb_fmask */ + } fs_sunx86; + struct { + __fs32 fs_sparecon[50];/* reserved for future constants */ + __fs32 fs_contigsumsize;/* size of cluster summary array */ + __fs32 fs_maxsymlinklen;/* max length of an internal symlink */ + __fs32 fs_inodefmt; /* format of on-disk inodes */ + __fs32 fs_maxfilesize[2]; /* max representable file size */ + __fs32 fs_qbmask[2]; /* ~usb_bmask */ + __fs32 fs_qfmask[2]; /* ~usb_fmask */ + __fs32 fs_state; /* file system state time stamp */ + } fs_44; + } fs_u2; + __fs32 fs_postblformat; /* format of positional layout tables */ + __fs32 fs_nrpos; /* number of rotational positions */ + __fs32 fs_postbloff; /* (__s16) rotation block list head */ + __fs32 fs_rotbloff; /* (__u8) blocks for each rotation */ + __fs32 fs_magic; /* magic number */ + __u8 fs_space[1]; /* list of blocks for each rotation */ +}; /*struct ufs_super_block*/ + +#endif diff --git a/libinstaller/syslxfs.h b/libinstaller/syslxfs.h index 4d8f3b2..7e46e23 100644 --- a/libinstaller/syslxfs.h +++ b/libinstaller/syslxfs.h @@ -12,7 +12,7 @@ #ifndef _SYSLXFS_H_ #define _SYSLXFS_H_ -/* Global fs_type for handling fat, ntfs, ext2/3/4, btrfs and xfs */ +/* Global fs_type for handling fat, ntfs, ext2/3/4, btrfs, xfs and ufs1/2 */ enum filesystem { NONE, EXT2, @@ -20,6 +20,8 @@ enum filesystem { VFAT, NTFS, XFS, + UFS1, + UFS2, }; extern int fs_type; -- 1.7.2.5
Raphael S.Carvalho
2014-May-29 23:35 UTC
[syslinux] [PATCH v2 2/2] core/fs: Add support to Unix File system 1/2.
From: Raphael S. Carvalho <raphael.scarv at gmail.com> It's already loading modules successfully, booting Linux, and both UFS version 1 and 2 seem to be working correctly. Signed-off-by: Raphael S. Carvalho <raphael.scarv at gmail.com> --- core/fs/ufs/bmap.c | 202 ++++++++++++++++++++++ core/fs/ufs/ufs.c | 486 ++++++++++++++++++++++++++++++++++++++++++++++++++++ core/fs/ufs/ufs.h | 254 +++++++++++++++++++++++++++ core/ldlinux.asm | 2 + 4 files changed, 944 insertions(+), 0 deletions(-) create mode 100644 core/fs/ufs/bmap.c create mode 100644 core/fs/ufs/ufs.c create mode 100644 core/fs/ufs/ufs.h diff --git a/core/fs/ufs/bmap.c b/core/fs/ufs/bmap.c new file mode 100644 index 0000000..7d8490f --- /dev/null +++ b/core/fs/ufs/bmap.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com> + * + * Partially taken from fs/ext2/bmap.c + * This file was modified according UFS1/2 needs. + * + * Copyright (C) 2009 Liu Aleaxander -- All rights reserved. This file + * may be redistributed under the terms of the GNU Public License. + */ + +#include <stdio.h> +#include <dprintf.h> +#include <fs.h> +#include <disk.h> +#include <cache.h> +#include "ufs.h" + +/* + * Copy blk address into buffer, this was needed since UFS1/2 addr size + * in blk maps differs from each other (32/64 bits respectivelly). + */ +static inline uint64_t +get_blkaddr (const uint8_t *blk, uint32_t index, uint32_t shift) +{ + uint64_t addr = 0; + + memcpy((uint8_t *) &addr, + (uint8_t *) blk + (index << shift), + 1 << shift); + + return addr; +} + +/* + * Scan forward in a range of blocks to see if they are contiguous, + * then return the initial value. + */ +static uint64_t +scan_set_nblocks(const uint8_t *map, uint32_t index, uint32_t addr_shift, + unsigned int count, size_t *nblocks) +{ + uint64_t addr; + uint64_t blk = get_blkaddr(map, index, addr_shift); + + /* + * Block spans 8 fragments, then address is interleaved by 8. + * This code works for either 32/64 sized addresses. + */ + if (nblocks) { + uint32_t skip = blk ? FRAGMENTS_PER_BLK : 0; + uint32_t next = blk + skip; + size_t cnt = 1; + + /* Get address of starting blk pointer */ + map += (index << addr_shift); + + ufs_debug("[scan] start blk: %u\n", blk); + ufs_debug("[scan] count (nr of blks): %u\n", count); + /* Go up to the end of blk map */ + while (--count) { + map += 1 << addr_shift; + addr = get_blkaddr(map, 0, addr_shift); +#if 0 + /* Extra debugging info (Too much prints) */ + ufs_debug("[scan] addr: %u next: %u\n", addr, next); +#endif + if (addr == next) { + cnt++; + next += skip; + } else { + break; + } + } + *nblocks = cnt; + ufs_debug("[scan] nblocks: %u\n", cnt); + ufs_debug("[scan] end blk: %u\n", next - FRAGMENTS_PER_BLK); + } + + return blk; +} + +/* + * The actual indirect block map handling - the block passed in should + * be relative to the beginning of the particular block hierarchy. + * + * @shft_per_blk: shift to get nr. of addresses in a block. + * @mask_per_blk: mask to limit the max nr. of addresses in a block. + * @addr_count: nr. of addresses in a block. + */ +static uint64_t +bmap_indirect(struct fs_info *fs, uint64_t start, uint32_t block, + int levels, size_t *nblocks) +{ + uint32_t shft_per_blk = fs->block_shift - UFS_SB(fs)->addr_shift; + uint32_t addr_count = (1 << shft_per_blk); + uint32_t mask_per_blk = addr_count - 1; + const uint8_t *blk = NULL; + uint32_t index = 0; + + while (levels--) { + if (!start) { + if (nblocks) + *nblocks = addr_count << (levels * shft_per_blk); + return 0; + } + + blk = get_cache(fs->fs_dev, frag_to_blk(fs, start)); + index = (block >> (levels * shft_per_blk)) & mask_per_blk; + start = get_blkaddr(blk, index, UFS_SB(fs)->addr_shift); + } + + return scan_set_nblocks(blk, index, UFS_SB(fs)->addr_shift, + addr_count - index, nblocks); +} + +/* + * Handle the traditional block map, like indirect, double indirect + * and triple indirect + */ +uint64_t ufs_bmap (struct inode *inode, block_t block, size_t *nblocks) +{ + uint32_t shft_per_blk, ptrs_per_blk; + static uint32_t indir_blks, double_blks, triple_blks; + struct fs_info *fs = inode->fs; + + /* Initialize static values */ + if (!indir_blks) { + shft_per_blk = fs->block_shift - UFS_SB(fs)->addr_shift; + ptrs_per_blk = fs->block_size >> UFS_SB(fs)->addr_shift; + + indir_blks = ptrs_per_blk; + double_blks = ptrs_per_blk << shft_per_blk; + triple_blks = double_blks << shft_per_blk; + } + + /* + * direct blocks + * (UFS2_ADDR_SHIFT) is also used for UFS1 because its direct ptr array + * was extended to 64 bits. + */ + if (block < UFS_DIRECT_BLOCKS) + return scan_set_nblocks((uint8_t *) PVT(inode)->direct_blk_ptr, + block, UFS2_ADDR_SHIFT, + UFS_DIRECT_BLOCKS - block, nblocks); + + /* indirect blocks */ + block -= UFS_DIRECT_BLOCKS; + if (block < indir_blks) + return bmap_indirect(fs, PVT(inode)->indirect_blk_ptr, + block, 1, nblocks); + + /* double indirect blocks */ + block -= indir_blks; + if (block < double_blks) + return bmap_indirect(fs, PVT(inode)->double_indirect_blk_ptr, + block, 2, nblocks); + + /* triple indirect blocks */ + block -= double_blks; + if (block < triple_blks) + return bmap_indirect(fs, PVT(inode)->triple_indirect_blk_ptr, + block, 3, nblocks); + + /* This can't happen... */ + return 0; +} + +/* + * Next extent for getfssec + * "Remaining sectors" means (lstart & blkmask). + */ +int ufs_next_extent(struct inode *inode, uint32_t lstart) +{ + struct fs_info *fs = inode->fs; + int blktosec = BLOCK_SHIFT(fs) - SECTOR_SHIFT(fs); + int frag_shift = BLOCK_SHIFT(fs) - UFS_SB(fs)->c_blk_frag_shift; + int blkmask = (1 << blktosec) - 1; + block_t block; + size_t nblocks = 0; + + ufs_debug("ufs_next_extent:\n"); + block = ufs_bmap(inode, lstart >> blktosec, &nblocks); + ufs_debug("blk: %u\n", block); + + if (!block) // Sparse block + inode->next_extent.pstart = EXTENT_ZERO; + else + /* + * Convert blk into sect addr and add the remaining + * sectors into pstart (sector start address). + */ + inode->next_extent.pstart + ((sector_t) (block << (frag_shift - SECTOR_SHIFT(fs)))) | + (lstart & blkmask); + + /* + * Subtract the remaining sectors from len since these sectors + * were added to pstart (sector start address). + */ + inode->next_extent.len = (nblocks << blktosec) - (lstart & blkmask); + return 0; +} \ No newline at end of file diff --git a/core/fs/ufs/ufs.c b/core/fs/ufs/ufs.c new file mode 100644 index 0000000..a7ef28f --- /dev/null +++ b/core/fs/ufs/ufs.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com> + * + * 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 <dprintf.h> +#include <stdio.h> +#include <string.h> +#include <sys/dirent.h> +#include <cache.h> +#include <disk.h> +#include <fs.h> +#include <minmax.h> +#include "core.h" +#include "ufs.h" + +/* + * Read the super block and check magic fields based on + * passed paramaters. + */ +static bool +do_checksb(struct ufs_super_block *sb, struct disk *disk, + const uint32_t sblock_off, const uint32_t ufs_smagic) +{ + uint32_t lba; + static uint32_t count; + + /* How many sectors are needed to fill sb struct */ + if (!count) + count = sizeof *sb >> disk->sector_shift; + /* Get lba address based on sector size of disk */ + lba = sblock_off >> (disk->sector_shift); + /* Read super block */ + disk->rdwr_sectors(disk, sb, lba, count, 0); + + if (sb->magic == ufs_smagic) + return true; + + return false; +} + +/* + * Go through all possible ufs superblock offsets. + * TODO: Add UFS support to removable media (sb offset: 0). + */ +static int +ufs_checksb(struct ufs_super_block *sb, struct disk *disk) +{ + /* Check for UFS1 sb */ + if (do_checksb(sb, disk, UFS1_SBLOCK_OFFSET, UFS1_SUPER_MAGIC)) + return UFS1; + /* Check for UFS2 sb */ + if (do_checksb(sb, disk, UFS2_SBLOCK_OFFSET, UFS2_SUPER_MAGIC)) + return UFS2; + /* UFS2 may also exist in 256k-, but this isn't the default */ + if (do_checksb(sb, disk, UFS2_SBLOCK2_OFFSET, UFS2_SUPER_MAGIC)) + return UFS2_PIGGY; + + return NONE; +} + +/* + * lblock stands for linear block address, + * whereas pblock is the actual blk ptr to get data from. + * + * UFS1/2 use frag addrs rather than blk ones, then + * the offset into the block must be calculated. + */ +static const void * +ufs_get_cache(struct inode *inode, block_t lblock) +{ + const void *data; + struct fs_info *fs = inode->fs; + struct ufs_sb_info *sb = UFS_SB(inode->fs); + uint64_t frag_addr, frag_offset; + uint32_t frag_shift; + block_t pblock; + + frag_addr = ufs_bmap(inode, lblock, NULL); + if (!frag_addr) + return NULL; + + frag_shift = fs->block_shift - sb->c_blk_frag_shift; + /* Get fragment byte address */ + frag_offset = frag_addr << frag_shift; + /* Convert frag addr to blk addr */ + pblock = frag_to_blk(fs, frag_addr); + /* Read the blk */ + data = get_cache(fs->fs_dev, pblock); + + /* Return offset into block */ + return data + (frag_offset & (fs->block_size - 1)); +} + +/* + * Based on fs/ext2/ext2.c + * find a dir entry, return it if found, or return NULL. + */ +static const struct ufs_dir_entry * +ufs_find_entry(struct fs_info *fs, struct inode *inode, const char *dname) +{ + const struct ufs_dir_entry *dir; + const char *data; + int32_t i, offset, maxoffset; + block_t index = 0; + + ufs_debug("ufs_find_entry: dname: %s ", dname); + for (i = 0; i < inode->size; i += fs->block_size) { + data = ufs_get_cache(inode, index++); + offset = 0; + maxoffset = min(inode->size-i, fs->block_size); + + /* The smallest possible size is 9 bytes */ + while (offset < maxoffset-8) { + dir = (const struct ufs_dir_entry *)(data + offset); + if (dir->dir_entry_len > maxoffset - offset) + break; + + /* + * Name fields are variable-length and null terminated, + * then it's possible to use strcmp directly. + */ + if (dir->inode_value && !strcmp(dname, (const char *)dir->name)) { + ufs_debug("(found)\n"); + return dir; + } + offset += dir->dir_entry_len; + } + } + ufs_debug("(not found)\n"); + return NULL; +} + +/* + * Get either UFS1/2 inode structures. + */ +static const void * +ufs_get_inode(struct fs_info *fs, int inr) +{ + const char *data; + uint32_t group, inode_offset, inode_table; + uint32_t block_num, block_off; + + /* Get cylinder group nr. */ + group = inr / UFS_SB(fs)->inodes_per_cg; + /* + * Ensuring group will not exceed the range 0:groups_count-1. + * By the way, this should *never* happen. + * Unless the (on-disk) fs structure is corrupted! + */ + if (group >= UFS_SB(fs)->groups_count) { + printf("ufs_get_inode: " + "group(%d) exceeded the avail. range (0:%d)\n", + group, UFS_SB(fs)->groups_count - 1); + return NULL; + } + + /* Offset into inode table of the cylinder group */ + inode_offset = inr % UFS_SB(fs)->inodes_per_cg; + /* Get inode table blk addr respective to cylinder group */ + inode_table = (group * UFS_SB(fs)->blocks_per_cg) + + UFS_SB(fs)->off_inode_tbl; + /* Calculating staggering offset (UFS1 only!) */ + if (UFS_SB(fs)->fs_type == UFS1) + inode_table += UFS_SB(fs)->ufs1.delta_value * + (group & UFS_SB(fs)->ufs1.cycle_mask); + + /* Get blk nr and offset into the blk */ + block_num = inode_table + inode_offset / UFS_SB(fs)->inodes_per_block; + block_off = inode_offset % UFS_SB(fs)->inodes_per_block; + + /* + * Read the blk from the blk addr previously computed; + * Calc the inode struct offset into the read block. + */ + data = get_cache(fs->fs_dev, block_num); + return data + block_off * UFS_SB(fs)->inode_size; +} + +static struct inode * +ufs1_iget_by_inr(struct fs_info *fs, uint32_t inr) +{ + const struct ufs1_inode *ufs_inode; + struct inode *inode; + uint64_t *dest; + uint32_t *source; + int i; + + ufs_inode = (struct ufs1_inode *) ufs_get_inode(fs, inr); + if (!ufs_inode) + return NULL; + + if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt)))) + return NULL; + + /* UFS1 doesn't support neither creation nor deletion times */ + inode->refcnt = ufs_inode->link_count; + inode->mode = IFTODT(ufs_inode->file_mode); + inode->size = ufs_inode->size; + inode->atime = ufs_inode->a_time; + inode->mtime = ufs_inode->m_time; + inode->blocks = ufs_inode->blocks_held; + inode->flags = ufs_inode->flags; + + /* + * Copy and extend blk pointers to 64 bits, so avoid + * having two structures for inode private. + */ + dest = (uint64_t *) inode->pvt; + source = (uint32_t *) ufs_inode->direct_blk_ptr; + for (i = 0; i < UFS_NBLOCKS; i++) + dest[i] = ((uint64_t) source[i]) & 0xFFFFFFFF; + + return inode; +} + +static struct inode * +ufs2_iget_by_inr(struct fs_info *fs, uint32_t inr) +{ + const struct ufs2_inode *ufs_inode; + struct inode *inode; + + ufs_inode = (struct ufs2_inode *) ufs_get_inode(fs, inr); + if (!ufs_inode) + return NULL; + + if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt)))) + return NULL; + + /* UFS2 doesn't support deletion time */ + inode->refcnt = ufs_inode->link_count; + inode->mode = IFTODT(ufs_inode->file_mode); + inode->size = ufs_inode->size; + inode->atime = ufs_inode->a_time; + inode->ctime = ufs_inode->creat_time; + inode->mtime = ufs_inode->m_time; + inode->blocks = ufs_inode->bytes_held >> fs->block_shift; + inode->flags = ufs_inode->flags; + memcpy(inode->pvt, ufs_inode->direct_blk_ptr, + sizeof(uint64_t) * UFS_NBLOCKS); + + return inode; +} + +/* + * Both ufs_iget_root and ufs_iget callback based on ufs type. + */ +static struct inode * +ufs_iget_root(struct fs_info *fs) +{ + return UFS_SB(fs)->ufs_iget_by_inr(fs, UFS_ROOT_INODE); +} + +static struct inode * +ufs_iget(const char *dname, struct inode *parent) +{ + const struct ufs_dir_entry *dir; + struct fs_info *fs = parent->fs; + + dir = ufs_find_entry(fs, parent, dname); + if (!dir) + return NULL; + + return UFS_SB(fs)->ufs_iget_by_inr(fs, dir->inode_value); +} + +static void ufs1_read_blkaddrs(struct inode *inode, char *buf) +{ + uint32_t dest[UFS_NBLOCKS]; + const uint64_t *source = (uint64_t *) (inode->pvt); + int i; + + /* Convert ufs_inode_pvt uint64_t fields into uint32_t + * Upper-half part of ufs1 private blk addrs are always supposed to be + * zero (it's previosuly extended by us), thus data isn't being lost. */ + for (i = 0; i < UFS_NBLOCKS; i++) { + if ((source[i] >> 32) != 0) { + /* This should never happen, but will not prevent anything + * from working. */ + ufs_debug("ufs1: inode->pvt[%d]: warning!\n", i); + } + + dest[i] = (uint32_t)(source[i] & 0xFFFFFFFF); + } + memcpy(buf, (const char *) dest, inode->size); +} + +static void ufs2_read_blkaddrs(struct inode *inode, char *buf) +{ + memcpy(buf, (const char *) (inode->pvt), inode->size); +} + +/* + * Taken from ext2/ext2.c. + * Read the entire contents of an inode into a memory buffer + */ +static int cache_get_file(struct inode *inode, void *buf, size_t bytes) +{ + struct fs_info *fs = inode->fs; + size_t block_size = BLOCK_SIZE(fs); + uint32_t index = 0; /* Logical block number */ + size_t chunk; + const char *data; + char *p = buf; + + if (inode->size > bytes) + bytes = inode->size; + + while (bytes) { + chunk = min(bytes, block_size); + data = ufs_get_cache(inode, index++); + memcpy(p, data, chunk); + + bytes -= chunk; + p += chunk; + } + + return 0; +} + +static int ufs_readlink(struct inode *inode, char *buf) +{ + struct fs_info *fs = inode->fs; + uint32_t i_symlink_limit; + + if (inode->size > BLOCK_SIZE(fs)) + return -1; /* Error! */ + + // TODO: use UFS_SB(fs)->maxlen_isymlink instead. + i_symlink_limit = ((UFS_SB(fs)->fs_type == UFS1) ? + sizeof(uint32_t) : sizeof(uint64_t)) * UFS_NBLOCKS; + ufs_debug("UFS_SB(fs)->maxlen_isymlink=%d", UFS_SB(fs)->maxlen_isymlink); + + if (inode->size <= i_symlink_limit) + UFS_SB(fs)->ufs_read_blkaddrs(inode, buf); + else + cache_get_file(inode, buf, inode->size); + + return inode->size; +} + +static inline enum dir_type_flags get_inode_mode(uint8_t type) +{ + switch(type) { + case UFS_DTYPE_FIFO: return DT_FIFO; + case UFS_DTYPE_CHARDEV: return DT_CHR; + case UFS_DTYPE_DIR: return DT_DIR; + case UFS_DTYPE_BLOCK: return DT_BLK; + case UFS_DTYPE_RFILE: return DT_REG; + case UFS_DTYPE_SYMLINK: return DT_LNK; + case UFS_DTYPE_SOCKET: return DT_SOCK; + case UFS_DTYPE_WHITEOUT: return DT_WHT; + default: return DT_UNKNOWN; + } +} + +/* + * Read one directory entry at a time + */ +static int ufs_readdir(struct file *file, struct dirent *dirent) +{ + struct fs_info *fs = file->fs; + struct inode *inode = file->inode; + const struct ufs_dir_entry *dir; + const char *data; + block_t index = file->offset >> fs->block_shift; + + if (file->offset >= inode->size) + return -1; /* End of file */ + + data = ufs_get_cache(inode, index); + dir = (const struct ufs_dir_entry *) + (data + (file->offset & (BLOCK_SIZE(fs) - 1))); + + dirent->d_ino = dir->inode_value; + dirent->d_off = file->offset; + dirent->d_reclen = offsetof(struct dirent, d_name) + dir->name_length + 1; + dirent->d_type = get_inode_mode(dir->file_type & 0x0F); + memcpy(dirent->d_name, dir->name, dir->name_length); + dirent->d_name[dir->name_length] = '\0'; + + file->offset += dir->dir_entry_len; /* Update for next reading */ + + return 0; +} + +static inline struct ufs_sb_info * +set_ufs_info(struct ufs_super_block *sb, int ufs_type) +{ + struct ufs_sb_info *sbi; + + sbi = malloc(sizeof *sbi); + if (!sbi) + malloc_error("ufs_sb_info structure"); + + /* Setting up UFS-dependent info */ + if (ufs_type == UFS1) { + sbi->inode_size = sizeof (struct ufs1_inode); + sbi->groups_count = sb->ufs1.nr_frags / sb->frags_per_cg; + sbi->ufs1.delta_value = sb->ufs1.delta_value; + sbi->ufs1.cycle_mask = sb->ufs1.cycle_mask; + sbi->ufs_iget_by_inr = ufs1_iget_by_inr; + sbi->ufs_read_blkaddrs = ufs1_read_blkaddrs; + sbi->addr_shift = UFS1_ADDR_SHIFT; + } else { // UFS2 or UFS2_PIGGY + sbi->inode_size = sizeof (struct ufs2_inode); + sbi->groups_count = sb->ufs2.nr_frags / sb->frags_per_cg; + sbi->ufs_iget_by_inr = ufs2_iget_by_inr; + sbi->ufs_read_blkaddrs = ufs2_read_blkaddrs; + sbi->addr_shift = UFS2_ADDR_SHIFT; + } + sbi->inodes_per_block = sb->block_size / sbi->inode_size; + sbi->inodes_per_cg = sb->inodes_per_cg; + sbi->blocks_per_cg = sb->frags_per_cg >> sb->c_blk_frag_shift; + sbi->off_inode_tbl = sb->off_inode_tbl >> sb->c_blk_frag_shift; + sbi->c_blk_frag_shift = sb->c_blk_frag_shift; + sbi->maxlen_isymlink = sb->maxlen_isymlink; + sbi->fs_type = ufs_type; + + return sbi; +} + +/* + * Init the fs metadata and return block size + */ +static int ufs_fs_init(struct fs_info *fs) +{ + struct disk *disk = fs->fs_dev->disk; + struct ufs_super_block sb; + struct cache *cs; + + int ufs_type = ufs_checksb(&sb, disk); + if (ufs_type == NONE) + return -1; + + ufs_debug("%s SB FOUND!\n", ufs_type == UFS1 ? "UFS1" : "UFS2"); + ufs_debug("Block size: %u\n", sb.block_size); + + fs->fs_info = (struct ufs_sb_info *) set_ufs_info(&sb, ufs_type); + fs->sector_shift = disk->sector_shift; + fs->sector_size = disk->sector_size; + fs->block_shift = sb.block_shift; + fs->block_size = sb.block_size; + + /* Initialize the cache, and force a clean on block zero */ + cache_init(fs->fs_dev, sb.block_shift); + cs = _get_cache_block(fs->fs_dev, 0); + memset(cs->data, 0, fs->block_size); + cache_lock_block(cs); + + /* For debug purposes */ + //ufs_checking(fs); + + //return -1; + return fs->block_shift; +} + +const struct fs_ops ufs_fs_ops = { + .fs_name = "ufs", + .fs_flags = FS_USEMEM | FS_THISIND, + .fs_init = ufs_fs_init, + .searchdir = NULL, + .getfssec = generic_getfssec, + .close_file = generic_close_file, + .mangle_name = generic_mangle_name, + .open_config = generic_open_config, + .readlink = ufs_readlink, + .readdir = ufs_readdir, + .iget_root = ufs_iget_root, + .iget = ufs_iget, + .next_extent = ufs_next_extent, +}; diff --git a/core/fs/ufs/ufs.h b/core/fs/ufs/ufs.h new file mode 100644 index 0000000..04e9f84 --- /dev/null +++ b/core/fs/ufs/ufs.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com> + * + * 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. +*/ + +#ifndef _UFS_H_ +#define _UFS_H_ + +#include <stdint.h> + +/* Sector addresses */ +#define UFS1_SBLOCK_OFFSET 8192 +#define UFS2_SBLOCK_OFFSET 65536 +#define UFS2_SBLOCK2_OFFSET 262144 + +#define UFS1_ADDR_SHIFT 2 +#define UFS2_ADDR_SHIFT 3 + +/* Super magic numbers */ +#define UFS1_SUPER_MAGIC (0x011954) +#define UFS2_SUPER_MAGIC (0x19540119) + +#define UFS_ROOT_INODE 2 + +#define UFS_DIRECT_BLOCKS 12 +#define UFS_INDIRECT_BLOCK 1 +#define UFS_DOUBLE_INDIRECT_BLOCK 1 +#define UFS_TRIPLE_INDIRECT_BLOCK 1 +/* Total number of block addr hold by inodes */ +#define UFS_NBLOCKS 15 + +/* Blocks span 8 fragments */ +#define FRAGMENTS_PER_BLK 8 + +/* UFS types */ +typedef enum { + NONE, + UFS1, + UFS2, + UFS2_PIGGY, +} ufs_t; + +/* + * UFS1/UFS2 SUPERBLOCK structure + * CG stands for Cylinder Group. + * + * Variables prepended with off store offsets relative to + * base address of a Cylinder Group (CG). + */ +struct ufs_super_block { // supporting either ufs1 or ufs2 + uint8_t unused[8]; + /* Offset values */ + uint32_t off_backup_sb; // Backup super block + uint32_t off_group_desc; // Group Descriptor + uint32_t off_inode_tbl; // Inode table + uint32_t off_data_block; // First data block + union { + struct { /* Used for UFS1 */ + uint32_t delta_value; // For calc staggering offset + uint32_t cycle_mask; // Mask for staggering offset + uint32_t last_written; // Last written time + uint32_t nr_frags; // Number of frags in FS + uint32_t storable_frags_nr; // Nr of frags that can store data + } ufs1; + uint8_t unused1[20]; + }; + uint32_t nr_cyl_groups; // Number of cylinder groups. + uint32_t block_size; // Block size in bytes. + uint32_t fragment_size; // Fragment size in bytes. + uint8_t unused2[16]; + uint32_t block_addr_mask; // to calculate the address + uint32_t frag_addr_mask; + uint32_t block_shift; // to calculate byte address + uint32_t frag_shift; + uint32_t nr_contiguous_blk; // max number of continuous blks to alloc + uint32_t nr_blks_per_cg; // max number of blks per cylinder group + uint32_t c_blk_frag_shift; // Bits to convert blk and frag address. + uint32_t c_frag_sect_shift; // Bits to convert frag and sect address. + uint32_t superblk_size; // Superblock size. + uint8_t unused3[76]; + uint32_t inodes_per_cg; // Inodes per cylinder group + uint32_t frags_per_cg; // Fragments per cylinder group + union { + struct { /* Used for UFS2 */ + uint8_t unused[888]; + uint64_t nr_frags; // Number of fragments in FS + uint8_t unused1[232]; + } ufs2; + uint8_t unused4[1128]; + }; + uint32_t maxlen_isymlink; // Max length of internal symlink + uint32_t inodes_format; // Format of inodes + uint8_t unused5[44]; + uint32_t magic; // Magic value + uint8_t pad[160]; // padding up to sector (512 bytes) boundary +} __attribute__((__packed__)); + +/* + * Info about UFS1/2 super block. + */ +struct ufs_sb_info { + uint32_t blocks_per_cg; // Blocks per cylinder group + uint32_t inodes_per_cg; // Inodes per cylinder group + uint32_t inode_size; + uint32_t inodes_per_block; // Inodes per block + struct { /* UFS1 only! */ + /* Values for calculating staggering offset */ + uint32_t delta_value; + uint32_t cycle_mask; + } ufs1; + uint32_t off_inode_tbl; // Inode table offset. + uint32_t groups_count; // Number of groups in the fs + uint32_t addr_shift; // 2 ^ addr_shift = size in bytes of default addr. + uint32_t c_blk_frag_shift; // Convert blk/frag addr (vice-versa) + uint32_t maxlen_isymlink; // Max length of internal symlink + struct inode *(*ufs_iget_by_inr)(struct fs_info *, uint32_t); + void (*ufs_read_blkaddrs)(struct inode *, char *); + ufs_t fs_type; // { UFS1, UFS2, UFS2_PIGGY } +}; + +/* + * Get super block info struct + */ +static inline struct ufs_sb_info *UFS_SB(struct fs_info *fs) +{ + return fs->fs_info; +} + +/* + * Convert frag addr to blk addr + */ +static inline block_t frag_to_blk(struct fs_info *fs, uint64_t frag) +{ + return frag >> UFS_SB(fs)->c_blk_frag_shift; +} + +/* + * UFS1 inode structures + */ +struct ufs1_inode { + uint16_t file_mode; + uint16_t link_count; + uint8_t unused[4]; + uint64_t size; + uint32_t a_time; // Access time + uint32_t a_time_nanosec; + uint32_t m_time; // Modified time + uint32_t m_time_nanosec; + uint32_t ch_time; // Change time + uint32_t ch_time_nanosec; + uint32_t direct_blk_ptr[12]; + uint32_t indirect_blk_ptr; + uint32_t double_indirect_blk_ptr; + uint32_t triple_indirect_blk_ptr; + uint32_t flags; // Status flags + uint32_t blocks_held; // Blocks held + uint32_t generation_nrb; // (NFS) + uint32_t used_id; + uint32_t group_id; + uint8_t unused1[8]; +} __attribute__((__packed__)); + +/* + * UFS2 inode structures + */ +struct ufs2_inode { + uint16_t file_mode; + uint16_t link_count; + uint32_t user_id; + uint32_t group_id; + uint32_t inode_blocksize; + uint64_t size; + uint64_t bytes_held; + uint64_t a_time; // Access time + uint64_t m_time; // Modified time + uint64_t ch_time; // Change time + uint64_t creat_time; // Creation time + uint32_t a_time_nanosec; + uint32_t m_time_nanosec; + uint32_t ch_time_nanosec; + uint32_t creat_time_nanosec; + uint32_t generation_nrb; // (NFS) + uint32_t kernel_flags; + uint32_t flags; + uint32_t ext_attr_size; // Extended attrib size. + uint64_t ext_direct_blk_ptrs[2]; // Ext. attrib blk pointers. + uint64_t direct_blk_ptr[12]; + uint64_t indirect_blk_ptr; + uint64_t double_indirect_blk_ptr; + uint64_t triple_indirect_blk_ptr; + uint8_t unused[24]; +} __attribute__((__packed__)); + +#define PVT(p) ((struct ufs_inode_pvt *) p->pvt) + +struct ufs_inode_pvt { + uint64_t direct_blk_ptr[12]; + uint64_t indirect_blk_ptr; + uint64_t double_indirect_blk_ptr; + uint64_t triple_indirect_blk_ptr; +}; + +struct ufs_dir_entry { + uint32_t inode_value; + uint16_t dir_entry_len; + uint8_t file_type; + uint8_t name_length; + uint8_t name[1]; // Dir names are null terminated!!! +} __attribute__((__packed__)); + +enum inode_type_flags { + UFS_INO_FIFO = 0x1000, + UFS_INO_CHARDEV = 0x2000, + UFS_INO_DIRECTORY = 0x4000, + UFS_INO_BLOCKDEV = 0x6000, + UFS_INO_RFILE = 0x8000, + UFS_INO_SYMLINK = 0xA000, + UFS_INO_UNIXSOCKET = 0xC000, +}; + +enum dir_type_flags { + UFS_DTYPE_UNKNOWN = 0, + UFS_DTYPE_FIFO = 1, + UFS_DTYPE_CHARDEV = 2, + UFS_DTYPE_DIR = 4, + UFS_DTYPE_BLOCK = 6, + UFS_DTYPE_RFILE = 8, + UFS_DTYPE_SYMLINK = 10, + UFS_DTYPE_SOCKET = 12, + UFS_DTYPE_WHITEOUT = 14, +}; + +/* Functions from bmap.c */ +extern uint64_t ufs_bmap (struct inode *, block_t, size_t *); +extern int ufs_next_extent(struct inode *, uint32_t); + +#define ufs_debug dprintf +//extern void ufs_checking (struct fs_info *); + +#endif /* _UFS_H_ */ diff --git a/core/ldlinux.asm b/core/ldlinux.asm index a1f96b7..050f503 100644 --- a/core/ldlinux.asm +++ b/core/ldlinux.asm @@ -43,6 +43,8 @@ ROOT_FS_OPS: dd xfs_fs_ops extern btrfs_fs_ops dd btrfs_fs_ops + extern ufs_fs_ops + dd ufs_fs_ops dd 0 %include "diskfs.inc" -- 1.7.2.5
Op 2014-05-29 om 20:35 schreef Raphael S.Carvalho:> From: Raphael S. Carvalho <raphael.scarv at gmail.com> > > Change since v1: > * Fix bug on dentry structure (thank you specification; btw, sarcasm), and > consequently a bug on ufs_readdir. > * Add readlink support (applied tests for symlinks whose destionation path > were stored in blk pointers and the file itself). > * Several improvements. > > Wrote the documentation below. I think it would be good to push the doc to > the wiki as soon as the UFS support gets merged. > > Unix Fast File System (UFS/FFS) 1/2 on Syslinux - (usage/install) > ----- > There is a confusion about the name of this file system, then I decided to > contact the author, Kirk McKusick, who replied: > "The name has always been confusing (my bad). The code is broken into two > parts, the part that handles naming (UFS where the U stands for Unix), and the > part that handles disk layout (FFS where the F stands for Fast). > When the two parts are put together they are called UFS/FFS or more commonly > just UFS." > > Dependencies: > - Environment: > Require a *GNU/Linux* system to follow these steps. > - Package: > ufsutils (UFS filesystems utilities) > - Device driver: > UFS support on Linux: you need a kernel with the option CONFIG_UFS_FS_WRITE > enabled or at least configured as a module. > For the latter case, load the module ufs.ko with modprobe (probably located > at: /lib/modules/). > If this option wasn't even configured as a module, then you need to recompile > your kernel with that option enabled. > Further information can be found in the link below: > http://oz123.github.io/writings/2012-11-26-enable-ufs-write-support-debian/ > > Creating an UFS image: > sudo dd if=/dev/zero of=<image> bs= count=; > - UFS1: > sudo mkfs.ufs -O 1 <image>; > - UFS2: > sudo mkfs.ufs <image>; > > Mounting an UFS image: > - UFS1: > sudo mount -t ufs -o rw,loop,ufstype=44bsd <image> <mountpoint>; > - UFS2: > sudo mount -t ufs -o rw,loop,ufstype=ufs2 <image> <mountpoint>; > > Installing syslinux on an UFS image: > - Install extlinux into your mount point: > sudo extlinux -i <mountpoint>; > > - Finally, set up the syslinux config file. > > > Thanks for pcacjr who introduced me to Syslinux :-) > > Raphael S. Carvalho (2): > installer: Add UFS1/2 support for Extlinux installer > core/fs: Add support to Unix File system 1/2. > > core/fs/ufs/bmap.c | 202 ++++++++++++++++++++ > core/fs/ufs/ufs.c | 486 ++++++++++++++++++++++++++++++++++++++++++++++++ > core/fs/ufs/ufs.h | 254 +++++++++++++++++++++++++ > core/ldlinux.asm | 2 + > extlinux/main.c | 51 ++++- > extlinux/ufs.h | 26 +++ > extlinux/ufs_fs.h | 307 ++++++++++++++++++++++++++++++ > libinstaller/syslxfs.h | 4 +- > 8 files changed, 1322 insertions(+), 10 deletions(-) > create mode 100644 core/fs/ufs/bmap.c > create mode 100644 core/fs/ufs/ufs.c > create mode 100644 core/fs/ufs/ufs.h > create mode 100644 extlinux/ufs.h > create mode 100644 extlinux/ufs_fs.h >FYI: Both patches are commited. Thanks for the code contritubion to Syslinux
Possibly Parallel Threads
- [PATCH 0/2] UFS1/2 support series
- [PATCH 2/2] core/fs: Add support for Unix File system 1/2.
- [PATCH 001/001] core/fs: Add support to Unix File system 1/2.
- [PATCH 001/001] Add UFS1/2 support to Extlinux installer.
- [PATCH 1/1 v2] Add UFS1/2 support to Extlinux installer.