1  /*
       2   *  mkfs.bfs - Create SCO BFS filesystem - aeb, 1999-09-07
       3   *
       4   *	Usage: mkfs.bfs [-N nr-of-inodes] [-V volume-name] [-F fsname] device
       5   */
       6  
       7  #include <errno.h>
       8  #include <fcntl.h>
       9  #include <getopt.h>
      10  #include <limits.h>
      11  #include <stdio.h>
      12  #include <stdlib.h>
      13  #include <string.h>
      14  #include <sys/stat.h>
      15  #include <time.h>
      16  #include <unistd.h>
      17  
      18  #include "blkdev.h"
      19  #include "c.h"
      20  #include "closestream.h"
      21  #include "nls.h"
      22  #include "strutils.h"
      23  #include "xalloc.h"
      24  #include "bitops.h"
      25  #include "exitcodes.h"
      26  
      27  #define BFS_ROOT_INO		2
      28  #define BFS_NAMELEN		14
      29  #define BFS_BLOCKSIZE		512
      30  #define BFS_SUPER_MAGIC		0x1badface
      31  
      32  /* superblock - 512 bytes */
      33  struct bfssb {
      34  	uint32_t s_magic;
      35  	uint32_t s_start;	/* byte offset of start of data */
      36  	uint32_t s_end;	/* sizeof(slice)-1 */
      37  
      38  	/* for recovery during compaction */
      39  	uint32_t s_from, s_to;	/* src and dest block of current transfer */
      40  	int32_t s_backup_from, s_backup_to;
      41  
      42  	/* labels - may well contain garbage */
      43  	char s_fsname[6];
      44  	char s_volume[6];
      45  	char s_pad[472];
      46  };
      47  
      48  /* inode - 64 bytes */
      49  struct bfsi {
      50  	uint16_t i_ino;
      51  	unsigned char i_pad1[2];
      52  	uint32_t i_first_block;
      53  	uint32_t i_last_block;
      54  	uint32_t i_bytes_to_end;
      55  	uint32_t i_type;	/* 1: file, 2: the unique dir */
      56  	uint32_t i_mode;
      57  	uint32_t i_uid, i_gid;
      58  	uint32_t i_nlinks;
      59  	uint32_t i_atime, i_mtime, i_ctime;
      60  	unsigned char i_pad2[16];
      61  };
      62  
      63  #define BFS_DIR_TYPE	2
      64  
      65  /* directory entry - 16 bytes */
      66  struct bfsde {
      67  	uint16_t d_ino;
      68  	char d_name[BFS_NAMELEN];
      69  };
      70  
      71  static void __attribute__((__noreturn__)) usage(void)
      72  {
      73  	FILE *out = stdout;
      74  	fprintf(out,
      75  		_("Usage: %s [options] device [block-count]\n"),
      76  		program_invocation_short_name);
      77  
      78  	fputs(USAGE_SEPARATOR, out);
      79  	fputs(_("Make an SCO bfs filesystem.\n"), out);
      80  
      81  	fprintf(out, _("\nOptions:\n"
      82  		       " -N, --inodes=NUM    specify desired number of inodes\n"
      83  		       " -V, --vname=NAME    specify volume name\n"
      84  		       " -F, --fname=NAME    specify file system name\n"
      85  		       " -v, --verbose       explain what is being done\n"
      86  		       " -c                  this option is silently ignored\n"
      87  		       " -l                  this option is silently ignored\n"
      88  		       " --lock[=<mode>]     use exclusive device lock (yes, no or nonblock)\n"
      89  		       ));
      90  	printf(USAGE_HELP_OPTIONS(21));
      91  
      92  	printf(USAGE_MAN_TAIL("mkfs.bfs(8)"));
      93  	exit(EXIT_SUCCESS);
      94  }
      95  
      96  int main(int argc, char **argv)
      97  {
      98  	char *device, *volume, *fsname;
      99  	char *lockmode = 0;
     100  	long inodes;
     101  	unsigned long long total_blocks, ino_bytes, ino_blocks, data_blocks;
     102  	unsigned long long user_specified_total_blocks = 0;
     103  	int verbose = 0;
     104  	int fd;
     105  	uint32_t first_block;
     106  	struct bfssb sb;
     107  	struct bfsi ri;
     108  	struct bfsde de;
     109  	struct stat statbuf;
     110  	time_t now;
     111  	int c, i, len;
     112  
     113  	enum {
     114  	    VERSION_OPTION = CHAR_MAX + 1,
     115  	    OPT_LOCK
     116  	};
     117  	static const struct option longopts[] = {
     118  		{"inodes", required_argument, NULL, 'N'},
     119  		{"vname", required_argument, NULL, 'V'},
     120  		{"fname", required_argument, NULL, 'F'},
     121  		{"verbose", no_argument, NULL, 'v'},
     122  		{"version", no_argument, NULL, VERSION_OPTION},
     123  		{"help", no_argument, NULL, 'h'},
     124  		{"lock", optional_argument, NULL, OPT_LOCK},
     125  		{NULL, 0, NULL, 0}
     126  	};
     127  
     128  	setlocale(LC_ALL, "");
     129  	bindtextdomain(PACKAGE, LOCALEDIR);
     130  	textdomain(PACKAGE);
     131  	close_stdout_atexit();
     132  
     133  	if (argc < 2) {
     134  		warnx(_("not enough arguments"));
     135  		errtryhelp(EXIT_FAILURE);
     136  	}
     137  	if (argc == 2 && !strcmp(argv[1], "-V"))
     138  		print_version(EXIT_SUCCESS);
     139  
     140  	volume = fsname = "      ";	/* is there a default? */
     141  	inodes = 0;
     142  
     143  	while ((c = getopt_long(argc, argv, "N:V:F:vhcl", longopts, NULL)) != -1) {
     144  		switch (c) {
     145  		case 'N':
     146  			inodes = strtol_or_err(optarg, _("invalid number of inodes"));
     147  			break;
     148  
     149  		case 'V':
     150  			len = strlen(optarg);
     151  			if (len <= 0 || len > 6)
     152  				errx(EXIT_FAILURE, _("volume name too long"));
     153  			volume = xstrdup(optarg);
     154  			break;
     155  
     156  		case 'F':
     157  			len = strlen(optarg);
     158  			if (len <= 0 || len > 6)
     159  				errx(EXIT_FAILURE, _("fsname name too long"));
     160  			fsname = xstrdup(optarg);
     161  			break;
     162  
     163  		case 'v':
     164  			verbose = 1;
     165  			break;
     166  
     167  		case 'c':
     168  		case 'l':
     169  			/* when called via mkfs we may get options c,l,v */
     170  			break;
     171  
     172                  case OPT_LOCK:
     173  			lockmode = "1";
     174  			if (optarg) {
     175  				if (*optarg == '=')
     176  					optarg++;
     177  				lockmode = optarg;
     178  			}
     179  			break;
     180  
     181  		case VERSION_OPTION:
     182  			print_version(EXIT_SUCCESS);
     183  		case 'h':
     184  			usage();
     185  		default:
     186  			errtryhelp(EXIT_FAILURE);
     187  		}
     188  	}
     189  
     190  	if (optind == argc) {
     191  		warnx(_("no device specified"));
     192  		errtryhelp(EXIT_FAILURE);
     193  	}
     194  
     195  	device = argv[optind++];
     196  
     197  	if (stat(device, &statbuf) < 0)
     198  		err(EXIT_FAILURE, _("stat of %s failed"), device);
     199  
     200  	fd = open_blkdev_or_file(&statbuf, device, O_RDWR);
     201  	if (fd < 0)
     202  		err(EXIT_FAILURE, _("cannot open %s"), device);
     203  
     204  	if (blkdev_lock(fd, device, lockmode) != 0)
     205  		exit(MKFS_EX_ERROR);
     206  
     207  	if (optind == argc - 1)
     208  		user_specified_total_blocks =
     209  			strtou64_or_err(argv[optind], _("invalid block-count"));
     210  	else if (optind != argc) {
     211  		warnx(_("bad usage"));
     212  		errtryhelp(EXIT_FAILURE);
     213  	}
     214  
     215  	if (blkdev_get_sectors(fd, &total_blocks) == -1) {
     216  		if (!user_specified_total_blocks)
     217  			err(EXIT_FAILURE, _("cannot get size of %s"), device);
     218  		total_blocks = user_specified_total_blocks;
     219  	} else if (user_specified_total_blocks) {
     220  		if (user_specified_total_blocks > total_blocks)
     221  			errx(EXIT_FAILURE,
     222  			     _("blocks argument too large, max is %llu"),
     223  			     total_blocks);
     224  		total_blocks = user_specified_total_blocks;
     225  	}
     226  
     227  	if (!inodes) {
     228  		/* pick some reasonable default */
     229  		inodes = 8 * (total_blocks / 800);
     230  		if (inodes < 48)
     231  			inodes = 48;
     232  		if (512 < inodes)
     233  			inodes = 512;
     234  	} else {
     235  		/* believe the user */
     236  		if (512 < inodes)
     237  			errx(EXIT_FAILURE, _("too many inodes - max is 512"));
     238  	}
     239  
     240  	ino_bytes = inodes * sizeof(struct bfsi);
     241  	ino_blocks = (ino_bytes + BFS_BLOCKSIZE - 1) / BFS_BLOCKSIZE;
     242  	data_blocks = total_blocks - ino_blocks - 1;
     243  
     244  	/* mimic the behavior of SCO's mkfs - maybe this limit is needed */
     245  	if (data_blocks < 32)
     246  		errx(EXIT_FAILURE,
     247  		     _("not enough space, need at least %llu blocks"),
     248  		     ino_blocks + 33);
     249  
     250  	memset(&sb, 0, sizeof(sb));
     251  	sb.s_magic = cpu_to_le32(BFS_SUPER_MAGIC);
     252  	sb.s_start = cpu_to_le32(ino_bytes + sizeof(struct bfssb));
     253  	sb.s_end = cpu_to_le32(total_blocks * BFS_BLOCKSIZE - 1);
     254  	sb.s_from = sb.s_to = sb.s_backup_from = sb.s_backup_to = -1;
     255  	memcpy(sb.s_fsname, fsname, 6);
     256  	memcpy(sb.s_volume, volume, 6);
     257  
     258  	if (verbose) {
     259  		fprintf(stderr, _("Device: %s\n"), device);
     260  		fprintf(stderr, _("Volume: <%-6s>\n"), volume);
     261  		fprintf(stderr, _("FSname: <%-6s>\n"), fsname);
     262  		fprintf(stderr, _("BlockSize: %d\n"), BFS_BLOCKSIZE);
     263  		if (ino_blocks == 1)
     264  			fprintf(stderr, _("Inodes: %ld (in 1 block)\n"),
     265  				inodes);
     266  		else
     267  			fprintf(stderr, _("Inodes: %ld (in %llu blocks)\n"),
     268  				inodes, ino_blocks);
     269  		fprintf(stderr, _("Blocks: %llu\n"), total_blocks);
     270  		fprintf(stderr, _("Inode end: %d, Data end: %d\n"),
     271  			le32_to_cpu(sb.s_start) - 1, le32_to_cpu(sb.s_end));
     272  	}
     273  
     274  	if (write(fd, &sb, sizeof(sb)) != sizeof(sb))
     275  		err(EXIT_FAILURE, _("error writing superblock"));
     276  
     277  	memset(&ri, 0, sizeof(ri));
     278  	ri.i_ino = cpu_to_le16(BFS_ROOT_INO);
     279  	first_block = 1 + ino_blocks;
     280  	ri.i_first_block = cpu_to_le32(first_block);
     281  	ri.i_last_block = cpu_to_le32(first_block +
     282  	    (inodes * sizeof(de) - 1) / BFS_BLOCKSIZE);
     283  	ri.i_bytes_to_end = cpu_to_le32(first_block * BFS_BLOCKSIZE
     284  	    + 2 * sizeof(struct bfsde) - 1);
     285  	ri.i_type = cpu_to_le32(BFS_DIR_TYPE);
     286  	ri.i_mode = cpu_to_le32(S_IFDIR | 0755);	/* or just 0755 */
     287  	ri.i_uid = cpu_to_le32(0);
     288  	ri.i_gid = cpu_to_le32(1);			/* random */
     289  	ri.i_nlinks = 2;
     290  	time(&now);
     291  	ri.i_atime = cpu_to_le32(now);
     292  	ri.i_mtime = cpu_to_le32(now);
     293  	ri.i_ctime = cpu_to_le32(now);
     294  
     295  	if (write(fd, &ri, sizeof(ri)) != sizeof(ri))
     296  		err(EXIT_FAILURE, _("error writing root inode"));
     297  
     298  	memset(&ri, 0, sizeof(ri));
     299  	for (i = 1; i < inodes; i++)
     300  		if (write(fd, &ri, sizeof(ri)) != sizeof(ri))
     301  			err(EXIT_FAILURE, _("error writing inode"));
     302  
     303  	if (lseek(fd, (1 + ino_blocks) * BFS_BLOCKSIZE, SEEK_SET) == -1)
     304  		err(EXIT_FAILURE, _("seek error"));
     305  
     306  	memset(&de, 0, sizeof(de));
     307  	de.d_ino = cpu_to_le16(BFS_ROOT_INO);
     308  	memcpy(de.d_name, ".", 1);
     309  	if (write(fd, &de, sizeof(de)) != sizeof(de))
     310  		err(EXIT_FAILURE, _("error writing . entry"));
     311  
     312  	memcpy(de.d_name, "..", 2);
     313  	if (write(fd, &de, sizeof(de)) != sizeof(de))
     314  		err(EXIT_FAILURE, _("error writing .. entry"));
     315  
     316  	if (close_fd(fd) != 0)
     317  		err(EXIT_FAILURE, _("error closing %s"), device);
     318  
     319  	return EXIT_SUCCESS;
     320  }