Home | History | Annotate | Download | only in ext2fs
      1 /*
      2  * mkjournal.c --- make a journal for a filesystem
      3  *
      4  * Copyright (C) 2000 Theodore Ts'o.
      5  *
      6  * %Begin-Header%
      7  * This file may be redistributed under the terms of the GNU Library
      8  * General Public License, version 2.
      9  * %End-Header%
     10  */
     11 
     12 #include "config.h"
     13 #include <stdio.h>
     14 #include <string.h>
     15 #if HAVE_UNISTD_H
     16 #include <unistd.h>
     17 #endif
     18 #if HAVE_ERRNO_H
     19 #include <errno.h>
     20 #endif
     21 #include <fcntl.h>
     22 #include <time.h>
     23 #if HAVE_SYS_STAT_H
     24 #include <sys/stat.h>
     25 #endif
     26 #if HAVE_SYS_TYPES_H
     27 #include <sys/types.h>
     28 #endif
     29 #if HAVE_SYS_IOCTL_H
     30 #include <sys/ioctl.h>
     31 #endif
     32 #if HAVE_NETINET_IN_H
     33 #include <netinet/in.h>
     34 #endif
     35 
     36 #include "ext2_fs.h"
     37 #include "e2p/e2p.h"
     38 #include "ext2fs.h"
     39 
     40 #include "kernel-jbd.h"
     41 
     42 /*
     43  * This function automatically sets up the journal superblock and
     44  * returns it as an allocated block.
     45  */
     46 errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
     47 					   __u32 num_blocks, int flags,
     48 					   char  **ret_jsb)
     49 {
     50 	errcode_t		retval;
     51 	journal_superblock_t	*jsb;
     52 
     53 	if (num_blocks < JFS_MIN_JOURNAL_BLOCKS)
     54 		return EXT2_ET_JOURNAL_TOO_SMALL;
     55 
     56 	if ((retval = ext2fs_get_mem(fs->blocksize, &jsb)))
     57 		return retval;
     58 
     59 	memset (jsb, 0, fs->blocksize);
     60 
     61 	jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
     62 	if (flags & EXT2_MKJOURNAL_V1_SUPER)
     63 		jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1);
     64 	else
     65 		jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
     66 	jsb->s_blocksize = htonl(fs->blocksize);
     67 	jsb->s_maxlen = htonl(num_blocks);
     68 	jsb->s_nr_users = htonl(1);
     69 	jsb->s_first = htonl(1);
     70 	jsb->s_sequence = htonl(1);
     71 	memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid));
     72 	/*
     73 	 * If we're creating an external journal device, we need to
     74 	 * adjust these fields.
     75 	 */
     76 	if (ext2fs_has_feature_journal_dev(fs->super)) {
     77 		jsb->s_nr_users = 0;
     78 		jsb->s_first = htonl(ext2fs_journal_sb_start(fs->blocksize) + 1);
     79 	}
     80 
     81 	*ret_jsb = (char *) jsb;
     82 	return 0;
     83 }
     84 
     85 /*
     86  * This function writes a journal using POSIX routines.  It is used
     87  * for creating external journals and creating journals on live
     88  * filesystems.
     89  */
     90 static errcode_t write_journal_file(ext2_filsys fs, char *filename,
     91 				    blk_t num_blocks, int flags)
     92 {
     93 	errcode_t	retval;
     94 	char		*buf = 0;
     95 	int		fd, ret_size;
     96 	blk_t		i;
     97 
     98 	if ((retval = ext2fs_create_journal_superblock(fs, num_blocks, flags,
     99 						       &buf)))
    100 		return retval;
    101 
    102 	/* Open the device or journal file */
    103 	if ((fd = open(filename, O_WRONLY)) < 0) {
    104 		retval = errno;
    105 		goto errfree;
    106 	}
    107 
    108 	/* Write the superblock out */
    109 	retval = EXT2_ET_SHORT_WRITE;
    110 	ret_size = write(fd, buf, fs->blocksize);
    111 	if (ret_size < 0) {
    112 		retval = errno;
    113 		goto errout;
    114 	}
    115 	if (ret_size != (int) fs->blocksize)
    116 		goto errout;
    117 	memset(buf, 0, fs->blocksize);
    118 
    119 	if (flags & EXT2_MKJOURNAL_LAZYINIT)
    120 		goto success;
    121 
    122 	for (i = 1; i < num_blocks; i++) {
    123 		ret_size = write(fd, buf, fs->blocksize);
    124 		if (ret_size < 0) {
    125 			retval = errno;
    126 			goto errout;
    127 		}
    128 		if (ret_size != (int) fs->blocksize)
    129 			goto errout;
    130 	}
    131 
    132 success:
    133 	retval = 0;
    134 errout:
    135 	close(fd);
    136 errfree:
    137 	ext2fs_free_mem(&buf);
    138 	return retval;
    139 }
    140 
    141 /*
    142  * Convenience function which zeros out _num_ blocks starting at
    143  * _blk_.  In case of an error, the details of the error is returned
    144  * via _ret_blk_ and _ret_count_ if they are non-NULL pointers.
    145  * Returns 0 on success, and an error code on an error.
    146  *
    147  * As a special case, if the first argument is NULL, then it will
    148  * attempt to free the static zeroizing buffer.  (This is to keep
    149  * programs that check for memory leaks happy.)
    150  */
    151 #define MAX_STRIDE_LENGTH (4194304 / (int) fs->blocksize)
    152 errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num,
    153 			      blk64_t *ret_blk, int *ret_count)
    154 {
    155 	int		j, count;
    156 	static void	*buf;
    157 	static int	stride_length;
    158 	errcode_t	retval;
    159 
    160 	/* If fs is null, clean up the static buffer and return */
    161 	if (!fs) {
    162 		if (buf) {
    163 			free(buf);
    164 			buf = 0;
    165 			stride_length = 0;
    166 		}
    167 		return 0;
    168 	}
    169 
    170 	/* Deal with zeroing less than 1 block */
    171 	if (num <= 0)
    172 		return 0;
    173 
    174 	/* Try a zero out command, if supported */
    175 	retval = io_channel_zeroout(fs->io, blk, num);
    176 	if (retval == 0)
    177 		return 0;
    178 
    179 	/* Allocate the zeroizing buffer if necessary */
    180 	if (num > stride_length && stride_length < MAX_STRIDE_LENGTH) {
    181 		void *p;
    182 		int new_stride = num;
    183 
    184 		if (new_stride > MAX_STRIDE_LENGTH)
    185 			new_stride = MAX_STRIDE_LENGTH;
    186 		p = realloc(buf, fs->blocksize * new_stride);
    187 		if (!p)
    188 			return EXT2_ET_NO_MEMORY;
    189 		buf = p;
    190 		stride_length = new_stride;
    191 		memset(buf, 0, fs->blocksize * stride_length);
    192 	}
    193 	/* OK, do the write loop */
    194 	j=0;
    195 	while (j < num) {
    196 		if (blk % stride_length) {
    197 			count = stride_length - (blk % stride_length);
    198 			if (count > (num - j))
    199 				count = num - j;
    200 		} else {
    201 			count = num - j;
    202 			if (count > stride_length)
    203 				count = stride_length;
    204 		}
    205 		retval = io_channel_write_blk64(fs->io, blk, count, buf);
    206 		if (retval) {
    207 			if (ret_count)
    208 				*ret_count = count;
    209 			if (ret_blk)
    210 				*ret_blk = blk;
    211 			return retval;
    212 		}
    213 		j += count; blk += count;
    214 	}
    215 	return 0;
    216 }
    217 
    218 errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num,
    219 			     blk_t *ret_blk, int *ret_count)
    220 {
    221 	blk64_t ret_blk2;
    222 	errcode_t retval;
    223 
    224 	retval = ext2fs_zero_blocks2(fs, blk, num, &ret_blk2, ret_count);
    225 	if (retval)
    226 		*ret_blk = (blk_t) ret_blk2;
    227 	return retval;
    228 }
    229 
    230 /*
    231  * Calculate the initial goal block to be roughly at the middle of the
    232  * filesystem.  Pick a group that has the largest number of free
    233  * blocks.
    234  */
    235 static blk64_t get_midpoint_journal_block(ext2_filsys fs)
    236 {
    237 	dgrp_t	group, start, end, i, log_flex;
    238 
    239 	group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) -
    240 					 fs->super->s_first_data_block) / 2);
    241 	log_flex = 1 << fs->super->s_log_groups_per_flex;
    242 	if (fs->super->s_log_groups_per_flex && (group > log_flex)) {
    243 		group = group & ~(log_flex - 1);
    244 		while ((group < fs->group_desc_count) &&
    245 		       ext2fs_bg_free_blocks_count(fs, group) == 0)
    246 			group++;
    247 		if (group == fs->group_desc_count)
    248 			group = 0;
    249 		start = group;
    250 	} else
    251 		start = (group > 0) ? group-1 : group;
    252 	end = ((group+1) < fs->group_desc_count) ? group+1 : group;
    253 	group = start;
    254 	for (i = start + 1; i <= end; i++)
    255 		if (ext2fs_bg_free_blocks_count(fs, i) >
    256 		    ext2fs_bg_free_blocks_count(fs, group))
    257 			group = i;
    258 	return ext2fs_group_first_block2(fs, group);
    259 }
    260 
    261 /*
    262  * This function creates a journal using direct I/O routines.
    263  */
    264 static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
    265 				     blk_t num_blocks, blk64_t goal, int flags)
    266 {
    267 	char			*buf;
    268 	errcode_t		retval;
    269 	struct ext2_inode	inode;
    270 	unsigned long long	inode_size;
    271 	int			falloc_flags = EXT2_FALLOCATE_FORCE_INIT;
    272 	blk64_t			zblk;
    273 
    274 	if ((retval = ext2fs_create_journal_superblock(fs, num_blocks, flags,
    275 						       &buf)))
    276 		return retval;
    277 
    278 	if ((retval = ext2fs_read_bitmaps(fs)))
    279 		goto out2;
    280 
    281 	if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
    282 		goto out2;
    283 
    284 	if (inode.i_blocks > 0) {
    285 		retval = EEXIST;
    286 		goto out2;
    287 	}
    288 
    289 	if (goal == ~0ULL)
    290 		goal = get_midpoint_journal_block(fs);
    291 
    292 	if (ext2fs_has_feature_extents(fs->super))
    293 		inode.i_flags |= EXT4_EXTENTS_FL;
    294 
    295 	if (!(flags & EXT2_MKJOURNAL_LAZYINIT))
    296 		falloc_flags |= EXT2_FALLOCATE_ZERO_BLOCKS;
    297 
    298 	inode_size = (unsigned long long)fs->blocksize * num_blocks;
    299 	inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0);
    300 	inode.i_links_count = 1;
    301 	inode.i_mode = LINUX_S_IFREG | 0600;
    302 	retval = ext2fs_inode_size_set(fs, &inode, inode_size);
    303 	if (retval)
    304 		goto out2;
    305 
    306 	retval = ext2fs_fallocate(fs, falloc_flags, journal_ino,
    307 				  &inode, goal, 0, num_blocks);
    308 	if (retval)
    309 		goto out2;
    310 
    311 	if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode)))
    312 		goto out2;
    313 
    314 	retval = ext2fs_bmap2(fs, journal_ino, &inode, NULL, 0, 0, NULL, &zblk);
    315 	if (retval)
    316 		goto out2;
    317 
    318 	retval = io_channel_write_blk64(fs->io, zblk, 1, buf);
    319 	if (retval)
    320 		goto out2;
    321 
    322 	memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
    323 	fs->super->s_jnl_blocks[15] = inode.i_size_high;
    324 	fs->super->s_jnl_blocks[16] = inode.i_size;
    325 	fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
    326 	ext2fs_mark_super_dirty(fs);
    327 
    328 out2:
    329 	ext2fs_free_mem(&buf);
    330 	return retval;
    331 }
    332 
    333 /*
    334  * Find a reasonable journal file size (in blocks) given the number of blocks
    335  * in the filesystem.  For very small filesystems, it is not reasonable to
    336  * have a journal that fills more than half of the filesystem.
    337  *
    338  * n.b. comments assume 4k blocks
    339  */
    340 int ext2fs_default_journal_size(__u64 num_blocks)
    341 {
    342 	if (num_blocks < 2048)
    343 		return -1;
    344 	if (num_blocks < 32768)		/* 128 MB */
    345 		return (1024);			/* 4 MB */
    346 	if (num_blocks < 256*1024)	/* 1 GB */
    347 		return (4096);			/* 16 MB */
    348 	if (num_blocks < 512*1024)	/* 2 GB */
    349 		return (8192);			/* 32 MB */
    350 	if (num_blocks < 4096*1024)	/* 16 GB */
    351 		return (16384);			/* 64 MB */
    352 	if (num_blocks < 8192*1024)	/* 32 GB */
    353 		return (32768);			/* 128 MB */
    354 	if (num_blocks < 16384*1024)	/* 64 GB */
    355 		return (65536);			/* 256 MB */
    356 	if (num_blocks < 32768*1024)	/* 128 GB */
    357 		return (131072);		/* 512 MB */
    358 	return 262144;				/* 1 GB */
    359 }
    360 
    361 int ext2fs_journal_sb_start(int blocksize)
    362 {
    363 	if (blocksize == EXT2_MIN_BLOCK_SIZE)
    364 		return 2;
    365 	return 1;
    366 }
    367 
    368 /*
    369  * This function adds a journal device to a filesystem
    370  */
    371 errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev)
    372 {
    373 	struct stat	st;
    374 	errcode_t	retval;
    375 	char		buf[SUPERBLOCK_SIZE];
    376 	journal_superblock_t	*jsb;
    377 	int		start;
    378 	__u32		i, nr_users;
    379 
    380 	/* Make sure the device exists and is a block device */
    381 	if (stat(journal_dev->device_name, &st) < 0)
    382 		return errno;
    383 
    384 	if (!S_ISBLK(st.st_mode))
    385 		return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */
    386 
    387 	/* Get the journal superblock */
    388 	start = ext2fs_journal_sb_start(journal_dev->blocksize);
    389 	if ((retval = io_channel_read_blk64(journal_dev->io, start,
    390 					    -SUPERBLOCK_SIZE,
    391 					    buf)))
    392 		return retval;
    393 
    394 	jsb = (journal_superblock_t *) buf;
    395 	if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
    396 	    (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2)))
    397 		return EXT2_ET_NO_JOURNAL_SB;
    398 
    399 	if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize)
    400 		return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
    401 
    402 	/* Check and see if this filesystem has already been added */
    403 	nr_users = ntohl(jsb->s_nr_users);
    404 	for (i=0; i < nr_users; i++) {
    405 		if (memcmp(fs->super->s_uuid,
    406 			   &jsb->s_users[i*16], 16) == 0)
    407 			break;
    408 	}
    409 	if (i >= nr_users) {
    410 		memcpy(&jsb->s_users[nr_users*16],
    411 		       fs->super->s_uuid, 16);
    412 		jsb->s_nr_users = htonl(nr_users+1);
    413 	}
    414 
    415 	/* Writeback the journal superblock */
    416 	if ((retval = io_channel_write_blk64(journal_dev->io, start,
    417 					    -SUPERBLOCK_SIZE, buf)))
    418 		return retval;
    419 
    420 	fs->super->s_journal_inum = 0;
    421 	fs->super->s_journal_dev = st.st_rdev;
    422 	memcpy(fs->super->s_journal_uuid, jsb->s_uuid,
    423 	       sizeof(fs->super->s_journal_uuid));
    424 	memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks));
    425 	ext2fs_set_feature_journal(fs->super);
    426 	ext2fs_mark_super_dirty(fs);
    427 	return 0;
    428 }
    429 
    430 /*
    431  * This function adds a journal inode to a filesystem, using either
    432  * POSIX routines if the filesystem is mounted, or using direct I/O
    433  * functions if it is not.
    434  */
    435 errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks,
    436 				    blk64_t goal, int flags)
    437 {
    438 	errcode_t		retval;
    439 	ext2_ino_t		journal_ino;
    440 	struct stat		st;
    441 	char			jfile[1024];
    442 	int			mount_flags;
    443 	int			fd = -1;
    444 
    445 	if (flags & EXT2_MKJOURNAL_NO_MNT_CHECK)
    446 		mount_flags = 0;
    447 	else if ((retval = ext2fs_check_mount_point(fs->device_name,
    448 						    &mount_flags,
    449 						    jfile, sizeof(jfile)-10)))
    450 		return retval;
    451 
    452 	if (mount_flags & EXT2_MF_MOUNTED) {
    453 #if HAVE_EXT2_IOCTLS
    454 		int f = 0;
    455 #endif
    456 		strcat(jfile, "/.journal");
    457 
    458 		/*
    459 		 * If .../.journal already exists, make sure any
    460 		 * immutable or append-only flags are cleared.
    461 		 */
    462 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
    463 		(void) chflags (jfile, 0);
    464 #else
    465 #if HAVE_EXT2_IOCTLS
    466 		fd = open(jfile, O_RDONLY);
    467 		if (fd >= 0) {
    468 			retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
    469 			close(fd);
    470 			if (retval)
    471 				return retval;
    472 		}
    473 #endif
    474 #endif
    475 
    476 		/* Create the journal file */
    477 		if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0)
    478 			return errno;
    479 
    480 		/* Note that we can't do lazy journal initialization for mounted
    481 		 * filesystems, since the zero writing is also allocating the
    482 		 * journal blocks.  We could use fallocate, but not all kernels
    483 		 * support that, and creating a journal on a mounted ext2
    484 		 * filesystems is extremely rare these days...  Ignore it. */
    485 		flags &= ~EXT2_MKJOURNAL_LAZYINIT;
    486 
    487 		if ((retval = write_journal_file(fs, jfile, num_blocks, flags)))
    488 			goto errout;
    489 
    490 		/* Get inode number of the journal file */
    491 		if (fstat(fd, &st) < 0) {
    492 			retval = errno;
    493 			goto errout;
    494 		}
    495 
    496 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
    497 		retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE);
    498 #else
    499 #if HAVE_EXT2_IOCTLS
    500 		if (ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) {
    501 			retval = errno;
    502 			goto errout;
    503 		}
    504 		f |= EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL;
    505 		retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
    506 #endif
    507 #endif
    508 		if (retval) {
    509 			retval = errno;
    510 			goto errout;
    511 		}
    512 
    513 		if (close(fd) < 0) {
    514 			retval = errno;
    515 			fd = -1;
    516 			goto errout;
    517 		}
    518 		journal_ino = st.st_ino;
    519 		memset(fs->super->s_jnl_blocks, 0,
    520 		       sizeof(fs->super->s_jnl_blocks));
    521 	} else {
    522 		if ((mount_flags & EXT2_MF_BUSY) &&
    523 		    !(fs->flags & EXT2_FLAG_EXCLUSIVE)) {
    524 			retval = EBUSY;
    525 			goto errout;
    526 		}
    527 		journal_ino = EXT2_JOURNAL_INO;
    528 		if ((retval = write_journal_inode(fs, journal_ino,
    529 						  num_blocks, goal, flags)))
    530 			return retval;
    531 	}
    532 
    533 	fs->super->s_journal_inum = journal_ino;
    534 	fs->super->s_journal_dev = 0;
    535 	memset(fs->super->s_journal_uuid, 0,
    536 	       sizeof(fs->super->s_journal_uuid));
    537 	ext2fs_set_feature_journal(fs->super);
    538 
    539 	ext2fs_mark_super_dirty(fs);
    540 	return 0;
    541 errout:
    542 	if (fd >= 0)
    543 		close(fd);
    544 	return retval;
    545 }
    546 
    547 errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
    548 {
    549 	return ext2fs_add_journal_inode2(fs, num_blocks, ~0ULL, flags);
    550 }
    551 
    552 
    553 #ifdef DEBUG
    554 main(int argc, char **argv)
    555 {
    556 	errcode_t	retval;
    557 	char		*device_name;
    558 	ext2_filsys	fs;
    559 
    560 	if (argc < 2) {
    561 		fprintf(stderr, "Usage: %s filesystem\n", argv[0]);
    562 		exit(1);
    563 	}
    564 	device_name = argv[1];
    565 
    566 	retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0,
    567 			      unix_io_manager, &fs);
    568 	if (retval) {
    569 		com_err(argv[0], retval, "while opening %s", device_name);
    570 		exit(1);
    571 	}
    572 
    573 	retval = ext2fs_add_journal_inode(fs, JFS_MIN_JOURNAL_BLOCKS, 0);
    574 	if (retval) {
    575 		com_err(argv[0], retval, "while adding journal to %s",
    576 			device_name);
    577 		exit(1);
    578 	}
    579 	retval = ext2fs_flush(fs);
    580 	if (retval) {
    581 		printf("Warning, had trouble writing out superblocks.\n");
    582 	}
    583 	ext2fs_close_free(&fs);
    584 	exit(0);
    585 
    586 }
    587 #endif
    588