changeset 53:41d55b5d49fd

Add start of mke2fs/gene2fs, and some other stuff I've been working on.
author Rob Landley <rob@landley.net>
date Sun, 14 Jan 2007 20:20:06 -0500
parents ba561c21ea27
children 60a7fb8ddeb4
files lib/functions.c lib/lib.h lib/portability.h main.c toys.h toys/mke2fs.c toys/toylist.h
diffstat 7 files changed, 404 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/lib/functions.c	Mon Jan 08 04:26:01 2007 -0500
+++ b/lib/functions.c	Sun Jan 14 20:20:06 2007 -0500
@@ -49,6 +49,7 @@
 	exit(toys.exitval);
 }
 
+
 // Die with an error message and strerror(errno)
 void perror_exit(char *msg, ...)
 {
@@ -61,6 +62,12 @@
 	exit(toys.exitval);
 }
 
+// Stub until the online help system goes in.
+void usage_exit(void)
+{
+	exit(1);
+}
+
 // Like strncpy but always null terminated.
 void strlcpy(char *dest, char *src, size_t size)
 {
@@ -213,11 +220,19 @@
 	return count;
 }
 
-// Die if we can't fill a buffer
-void xread(int fd, void *buf, size_t count)
+// Die if there's an error other than EOF.
+size_t xread(int fd, void *buf, size_t count)
 {
-	if (count != readall(fd, buf, count)) perror_exit("xread");
-}	
+	count = reread(fd, buf, count);
+	if (count < 0) perror_exit("xread");
+
+	return count;
+}
+
+void xreadall(int fd, void *buf, size_t count)
+{
+	if (count != readall(fd, buf, count)) perror_exit("xreadall");
+}
 
 void xwrite(int fd, void *buf, size_t count)
 {
@@ -377,3 +392,103 @@
 
 	return itoa_buf;
 }
+
+/*
+ This might be of use or might not.  Unknown yet...
+
+
+// Return how long the file at fd is, if there's any way to determine it.
+off_t fdlength(int fd)
+{
+	off_t bottom = 0, top = 0, pos;
+	long size;
+
+	// If the ioctl works for this, return it.
+
+	if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512;
+
+	// If not, do a binary search for the last location we can read.  (Some
+	// block devices don't do BLKGETSIZE right.)  This should probably have
+	// a CONFIG option...
+
+	do {
+		char temp;
+
+		pos = bottom + (top - bottom) / 2;
+
+		// If we can read from the current location, it's bigger.
+
+		if (lseek(fd, pos, 0)>=0 && safe_read(fd, &temp, 1)==1) {
+			if (bottom == top) bottom = top = (top+1) * 2;
+			else bottom = pos;
+
+			// If we can't, it's smaller.
+
+		} else {
+			if (bottom == top) {
+				if (!top) return 0;
+				bottom = top/2;
+			} else top = pos;
+		}
+	} while (bottom + 1 != top);
+
+	return pos + 1;
+}
+
+// Read contents of file as a single freshly allocated nul-terminated string.
+char *readfile(char *name)
+{
+	off_t len;
+	int fd;
+	char *buf;
+
+	fd = open(pidfile, O_RDONLY);
+	if (fd == -1) return 0;
+	len = fdlength(fd);
+	buf = xmalloc(len+1);
+	buf[xread(fd, buf, len)] = 0;
+
+	return buf;
+}
+
+char *xreadfile(char *name)
+{
+	char *buf = readfile(name);
+	if (!buf) error_exit("xreadfile %s", name);
+	return buf;
+}
+
+*/
+
+// Open a /var/run/NAME.pid file, dying if we can't write it or if it currently
+// exists and is this executable.
+void xpidfile(char *name)
+{
+	char pidfile[256], spid[32];
+	int i, fd;
+	pid_t pid;
+	
+	sprintf(pidfile, "/var/run/%s.pid", name);
+	// Try three times to open the sucker.
+	for (i=0; i<3; i++) {
+		fd = open(pidfile, O_CREAT|O_EXCL, 0644);
+		if (fd != -1) break;
+
+		// If it already existed, read it.  Loop for race condition.
+		fd = open(pidfile, O_RDONLY);
+		if (fd == -1) continue;
+
+		// Is the old program still there?
+		spid[xread(fd, spid, sizeof(spid)-1)] = 0;
+		close(fd);
+		pid = atoi(spid);
+		if (fd < 1 || kill(pid, 0) == ESRCH) unlink(pidfile);
+
+		// An else with more sanity checking might be nice here.
+	}
+	
+	if (i == 3) error_exit("xpidfile %s", name);
+
+	xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid()));
+	close(fd);
+}
--- a/lib/lib.h	Mon Jan 08 04:26:01 2007 -0500
+++ b/lib/lib.h	Sun Jan 14 20:20:06 2007 -0500
@@ -30,6 +30,7 @@
 void perror_msg(char *msg, ...);
 void error_exit(char *msg, ...);
 void perror_exit(char *msg, ...);
+void usage_exit(void);
 void strlcpy(char *dest, char *src, size_t size);
 void *xmalloc(size_t size);
 void *xzalloc(size_t size);
@@ -46,7 +47,8 @@
 ssize_t rewrite(int fd, void *buf, size_t count);
 ssize_t readall(int fd, void *buf, size_t count);
 ssize_t writeall(int fd, void *buf, size_t count);
-void xread(int fd, void *buf, size_t count);
+size_t xread(int fd, void *buf, size_t count);
+void xreadall(int fd, void *buf, size_t count);
 void xwrite(int fd, void *buf, size_t count);
 char *xgetcwd(void);
 char *xabspath(char *path);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/portability.h	Sun Jan 14 20:20:06 2007 -0500
@@ -0,0 +1,21 @@
+#include <endian.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define IS_LITTLE_ENDIAN 0
+#define SWAP_BE16(x) (x)
+#define SWAP_BE32(x) (x)
+#define SWAP_BE64(x) (x)
+#define SWAP_LE16(x) bswap_16(x)
+#define SWAP_LE32(x) bswap_32(x)
+#define SWAP_LE64(x) bswap_64(x)
+#else
+#define IS_LITTLE_ENDIAN 1
+#define IS_BIG_ENDIAN 0
+#define SWAP_BE16(x) bswap_16(x)
+#define SWAP_BE32(x) bswap_32(x)
+#define SWAP_BE64(x) bswap_64(x)
+#define SWAP_LE16(x) (x)
+#define SWAP_LE32(x) (x)
+#define SWAP_LE64(x) (x)
+#endif
--- a/main.c	Mon Jan 08 04:26:01 2007 -0500
+++ b/main.c	Sun Jan 14 20:20:06 2007 -0500
@@ -18,6 +18,7 @@
 // global context for this applet.
 
 struct toy_context toys;
+char toybuf[4096];
 
 struct toy_list *toy_find(char *name)
 {
--- a/toys.h	Mon Jan 08 04:26:01 2007 -0500
+++ b/toys.h	Sun Jan 14 20:20:06 2007 -0500
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
 #include <sys/types.h>
@@ -25,6 +26,7 @@
 #include <unistd.h>
 
 #include "lib/lib.h"
+#include "lib/portability.h"
 #include "gen_config.h"
 #include "toys/toylist.h"
 
@@ -46,4 +48,4 @@
 
 // One big temporary buffer, for use by applets (not library functions).
 
-char toybuf[4096];
+extern char toybuf[4096];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/mke2fs.c	Sun Jan 14 20:20:06 2007 -0500
@@ -0,0 +1,241 @@
+/* vi: set ts=4:
+ *
+ * mke2fs.c - Create an ext2 filesystem image.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+#include "toys.h"
+
+// Stuff defined in linux/ext2_fs.h
+
+#define EXT2_SUPER_MAGIC  0xEF53
+
+struct ext2_inode {
+	uint16_t mode;        // File mode
+	uint16_t uid;         // Low 16 bits of Owner Uid
+	uint32_t size;        // Size in bytes
+	uint32_t atime;       // Access time
+	uint32_t ctime;       // Creation time
+	uint32_t mtime;       // Modification time
+	uint32_t dtime;       // Deletion Time
+	uint16_t gid;         // Low 16 bits of Group Id
+	uint16_t links_count; // Links count
+	uint32_t blocks;      // Blocks count
+	uint32_t flags;       // File flags
+	uint32_t reserved1;
+	uint32_t block[15];   // Pointers to blocks
+	uint32_t generation;  // File version (for NFS)
+	uint32_t file_acl;    // File ACL
+	uint32_t dir_acl;     // Directory ACL
+	uint32_t faddr;       // Fragment address
+	uint8_t  frag;        // Fragment number
+	uint8_t  fsize;       // Fragment size
+	uint16_t pad1;
+	uint16_t uid_high;    // High bits of uid
+	uint16_t gid_high;    // High bits of gid
+	uint32_t reserved2;
+};
+
+struct ext2_super_block {
+	uint32_t inodes_count;      // Inodes count
+	uint32_t blocks_count;      // Blocks count
+	uint32_t r_blocks_count;    // Reserved blocks count
+	uint32_t free_blocks_count; // Free blocks count
+	uint32_t free_inodes_count; // Free inodes count
+	uint32_t first_data_block;  // First Data Block
+	uint32_t log_block_size;    // Block size
+	uint32_t log_frag_size;     // Fragment size
+	uint32_t blocks_per_group;  // # Blocks per group
+	uint32_t frags_per_group;   // # Fragments per group
+	uint32_t inodes_per_group;  // # Inodes per group
+	uint32_t mtime;             // Mount time
+	uint32_t wtime;             // Write time
+	uint16_t mnt_count;         // Mount count
+	uint16_t max_mnt_count;     // Maximal mount count
+	uint16_t magic;             // Magic signature
+	uint16_t state;             // File system state
+	uint16_t errors;            // Behaviour when detecting errors
+	uint16_t minor_rev_level;   // minor revision level
+	uint32_t lastcheck;         // time of last check
+	uint32_t checkinterval;     // max. time between checks
+	uint32_t creator_os;        // OS
+	uint32_t rev_level;         // Revision level
+	uint16_t def_resuid;        // Default uid for reserved blocks
+	uint16_t def_resgid;        // Default gid for reserved blocks
+	uint32_t first_ino;         // First non-reserved inode
+	uint16_t inode_size;        // size of inode structure
+	uint16_t block_group_nr;    // block group # of this superblock
+	uint32_t feature_compat;    // compatible feature set
+	uint32_t feature_incompat;  // incompatible feature set
+	uint32_t feature_ro_compat; // readonly-compatible feature set
+	char     uuid[16];          // 128-bit uuid for volume
+	char     volume_name[16];   // volume name
+	char     last_mounted[64];  // directory where last mounted
+	uint32_t alg_usage_bitmap;  // For compression
+	// For EXT2_COMPAT_PREALLOC
+	uint8_t  prealloc_blocks;   // Nr of blocks to try to preallocate
+	uint8_t  prealloc_dir_blocks; //Nr to preallocate for dirs
+	uint16_t padding1;
+	// For EXT3_FEATURE_COMPAT_HAS_JOURNAL
+	uint8_t  journal_uuid[16];   // uuid of journal superblock
+	uint32_t journal_inum;       // inode number of journal file
+	uint32_t journal_dev;        // device number of journal file
+	uint32_t last_orphan;        // start of list of inodes to delete
+	uint32_t hash_seed[4];       // HTREE hash seed
+	uint8_t  def_hash_version;   // Default hash version to use
+	uint8_t  padding2[3];
+	uint32_t default_mount_opts;
+ 	uint32_t first_meta_bg;      // First metablock block group
+	uint32_t reserved[190];      // Padding to the end of the block
+};
+
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC	0x0001
+#define EXT2_FEATURE_COMPAT_IMAGIC_INODES	0x0002
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL		0x0004
+#define EXT2_FEATURE_COMPAT_EXT_ATTR		0x0008
+#define EXT2_FEATURE_COMPAT_RESIZE_INO		0x0010
+#define EXT2_FEATURE_COMPAT_DIR_INDEX		0x0020
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER	0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR	0x0004
+
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION	0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE		0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER		0x0004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV	0x0008
+#define EXT2_FEATURE_INCOMPAT_META_BG		0x0010
+
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry_2 {
+	uint32_t inode;         // Inode number
+    uint16_t rec_len;       // Directory entry length
+    uint8_t  name_len;      // Name length
+    uint8_t  file_type;
+	char     name[255];     // File name
+};
+
+// Ext2 directory file types.  Only the low 3 bits are used.  The
+// other bits are reserved for now.
+
+enum {
+	EXT2_FT_UNKNOWN,
+	EXT2_FT_REG_FILE,
+	EXT2_FT_DIR,
+	EXT2_FT_CHRDEV,
+	EXT2_FT_BLKDEV,
+	EXT2_FT_FIFO,
+	EXT2_FT_SOCK,
+	EXT2_FT_SYMLINK,
+	EXT2_FT_MAX
+};
+
+
+	// b - block size (1024, 2048, 4096)
+	// F - force (run on mounted device or non-block device)
+	// i - bytes per inode 
+	// N - number of inodes
+	// m - reserved blocks percentage
+	// n - Don't write
+	// q - quiet
+
+	// L - volume label
+	// M - last mounted path
+	// o - creator os
+	
+	// j - create journal
+	// J - journal options (size=1024-102400 blocks,device=)
+	//        device=/dev/blah or LABEL=label UUID=uuid
+
+	// E - extended options (stride=stripe-size blocks)
+	// O - none,dir_index,filetype,has_journal,journal_dev,sparse_super
+
+
+// This is what's in a UUID according to the spec at
+// http://www.opengroup.org/onlinepubs/9629399/apdxa.htm
+
+//struct uuid {
+//	uint32_t  time_low;
+//	uint16_t  time_mid;
+//	uint16_t  time_hi_and_version;
+//	uint8_t   clock_seq_hi_and_reserved;
+//	uint8_t   clock_seq_low;
+//	uint8_t   node[6];
+//};
+
+
+// According to http://www.opengroup.org/onlinepubs/9629399/apdxa.htm
+// we should generate a uuid structure by reading a clock with 100 nanosecond
+// precision, normalizing it to the start of the gregorian calendar in 1582,
+// and looking up our eth0 mac address.
+//
+// On the other hand, we have 128 bits to come up with a unique identifier, of
+// which 6 have a defined value.  /dev/urandom it is.
+
+void create_uuid(char *uuid)
+{
+	// Read 128 random bytes
+	int fd = xopen("/dev/urandom", O_RDONLY);
+	xreadall(fd, uuid, 16);
+	close(fd);
+
+	// Claim to be a DCE format UUID.
+	uuid[6] = (uuid[6] & 0x0F) | 0x40;
+	uuid[8] = (uuid[8] & 0x3F) | 0x80;
+
+    // rfc2518 section 6.4.1 suggests if we're not using a macaddr, we should
+	// set bit 1 of the node ID, which is the mac multicast bit.  This means we
+	// should never collide with anybody actually using a macaddr.
+	uuid[11] = uuid[11] | 128;
+}
+
+int mke2fs_main(void)
+{
+	struct ext2_super_block *sb = xzalloc(sizeof(struct ext2_super_block));
+	int temp;
+
+	// Handle command line arguments.
+
+	if (!*toys.optargs || (!CFG_MKE2FS_GEN && toys.optargs[1])) usage_exit();
+	if (CFG_MKE2FS_GEN && toys.optargs[1]) {
+			temp = O_RDWR|O_CREAT;
+			xaccess(toys.optargs[1], R_OK);
+	} else temp = O_RDWR;
+	if (toy.mke2fs.blocksize!=1024 && toy.mke2fs.blocksize!=2048
+		&& toy.mke2fs.blocksize!=4096) error_exit("bad blocksize");
+
+	// For mke?fs, open file.  For gene?fs, create file.
+	toy.mke2fs.fsfd = xcreate(*toys.optargs, temp, 0777);
+
+    // We don't autodetect block size from external journaling devices, instead
+	// we write our block size to that journaling device.  (If they want a
+	// specific block size, they have the -b option.)
+
+// What's the deal with fs_type?
+// line 1059
+
+	// We skip the first 1k (to avoid the boot sector, if any).  Use this to
+	// figure out if this file is seekable.
+	if(-1 == lseek(toy.mke2fs.fsfd, 1024, SEEK_SET)) {
+		toy.mke2fs.noseek=1;
+		xwrite(toy.mke2fs.fsfd, sb, 1024);
+	}
+
+	// Fill out superblock structure
+
+	sb->rev_level = SWAP_LE32(1);
+	sb->feature_incompat = SWAP_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE);
+	sb->feature_ro_compat = SWAP_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
+
+	// If we're called as mke3fs or mkfs.ext3, do a journal.
+
+	if (strchr(toys.which->name,'3'))
+		sb->feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+
+	// Write superblock to disk.	
+	xwrite(toy.mke2fs.fsfd, sb, 3072); // 4096-1024
+
+	return 0;
+}
--- a/toys/toylist.h	Mon Jan 08 04:26:01 2007 -0500
+++ b/toys/toylist.h	Sun Jan 14 20:20:06 2007 -0500
@@ -22,11 +22,25 @@
 
 struct df_data {
 	struct arg_list *fstype;
+
 	long units;
 };
 
+struct mke2fs_data {
+	long blocksize;
+	long bytes_per_inode;
+	long inodes;
+	long reserved_percent;
+
+	int fsfd, noseek;
+};
+
+// "E:jJ:L:m:O:"
+#define MKE2FS_OPTSTRING "Fnqm:N:i:b:"
+
 union toy_union {
 	struct df_data df;
+	struct mke2fs_data mke2fs;
 } toy;
 
 #define TOYFLAG_USR      (1<<0)
@@ -47,7 +61,7 @@
 
 // List of all the applets toybox can provide.
 
-// This one is out of order on purpose.
+// This one is out of order on purpose: it's the first element in the array.
 
 NEWTOY(toybox, NULL, 0)
 
@@ -58,6 +72,7 @@
 USE_DF(NEWTOY(df, "Pkt*a", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_TOYSH(NEWTOY(exit, NULL, TOYFLAG_NOFORK))
 USE_HELLO(NEWTOY(hello, NULL, TOYFLAG_NOFORK|TOYFLAG_USR))
+USE_MKE2FS(NEWTOY(mke2fs, MKE2FS_OPTSTRING, TOYFLAG_SBIN))
 USE_ONEIT(NEWTOY(oneit, "+p<1", TOYFLAG_SBIN))
 USE_PWD(NEWTOY(pwd, NULL, TOYFLAG_BIN))
 USE_TOYSH(OLDTOY(sh, toysh, "c:i", TOYFLAG_BIN))