#include "config.h"
#include <stdio.h>
#include <string.h>
+#include <assert.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
#include "ext2_fs.h"
#include "e2p/e2p.h"
* This function automatically sets up the journal superblock and
* returns it as an allocated block.
*/
-errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
- __u32 num_blocks, int flags,
- char **ret_jsb)
+errcode_t ext2fs_create_journal_superblock2(ext2_filsys fs,
+ struct ext2fs_journal_params *jparams,
+ int flags, char **ret_jsb)
{
errcode_t retval;
journal_superblock_t *jsb;
- if (num_blocks < JBD2_MIN_JOURNAL_BLOCKS)
+ if (jparams->num_journal_blocks < JBD2_MIN_JOURNAL_BLOCKS)
return EXT2_ET_JOURNAL_TOO_SMALL;
if ((retval = ext2fs_get_mem(fs->blocksize, &jsb)))
else
jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V2);
jsb->s_blocksize = htonl(fs->blocksize);
- jsb->s_maxlen = htonl(num_blocks);
+ jsb->s_maxlen = htonl(jparams->num_journal_blocks + jparams->num_fc_blocks);
jsb->s_nr_users = htonl(1);
jsb->s_first = htonl(1);
jsb->s_sequence = htonl(1);
+ jsb->s_num_fc_blks = htonl(jparams->num_fc_blocks);
memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid));
/*
* If we're creating an external journal device, we need to
return 0;
}
+errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, __u32 num_blocks,
+ int flags, char **ret_sb)
+{
+ struct ext2fs_journal_params jparams;
+
+ jparams.num_journal_blocks = num_blocks;
+ jparams.num_fc_blocks = 0;
+
+ return ext2fs_create_journal_superblock2(fs, &jparams, flags, ret_sb);
+}
+
/*
* This function writes a journal using POSIX routines. It is used
* for creating external journals and creating journals on live
* filesystems.
*/
static errcode_t write_journal_file(ext2_filsys fs, char *filename,
- blk_t num_blocks, int flags)
+ struct ext2fs_journal_params *jparams,
+ int flags)
{
errcode_t retval;
char *buf = 0;
int fd, ret_size;
blk_t i;
- if ((retval = ext2fs_create_journal_superblock(fs, num_blocks, flags,
+ if ((retval = ext2fs_create_journal_superblock2(fs, jparams, flags,
&buf)))
return retval;
if (flags & EXT2_MKJOURNAL_LAZYINIT)
goto success;
- for (i = 1; i < num_blocks; i++) {
+ for (i = 1; i < jparams->num_journal_blocks + jparams->num_fc_blocks; i++) {
ret_size = write(fd, buf, fs->blocksize);
if (ret_size < 0) {
retval = errno;
* This function creates a journal using direct I/O routines.
*/
static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
- blk_t num_blocks, blk64_t goal, int flags)
+ struct ext2fs_journal_params *jparams,
+ blk64_t goal, int flags)
{
char *buf;
errcode_t retval;
int falloc_flags = EXT2_FALLOCATE_FORCE_INIT;
blk64_t zblk;
- if ((retval = ext2fs_create_journal_superblock(fs, num_blocks, flags,
+ if ((retval = ext2fs_create_journal_superblock2(fs, jparams, flags,
&buf)))
return retval;
if (!(flags & EXT2_MKJOURNAL_LAZYINIT))
falloc_flags |= EXT2_FALLOCATE_ZERO_BLOCKS;
- inode_size = (unsigned long long)fs->blocksize * num_blocks;
+ inode_size = (unsigned long long)fs->blocksize *
+ (jparams->num_journal_blocks + jparams->num_fc_blocks);
inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0);
inode.i_links_count = 1;
inode.i_mode = LINUX_S_IFREG | 0600;
goto out2;
retval = ext2fs_fallocate(fs, falloc_flags, journal_ino,
- &inode, goal, 0, num_blocks);
+ &inode, goal, 0,
+ jparams->num_journal_blocks + jparams->num_fc_blocks);
if (retval)
goto out2;
return retval;
}
+#ifdef __linux__
+static int parse_version_number(const char *s)
+{
+ int major, minor, rev;
+ char *endptr;
+ const char *cp = s;
+
+ if (!s)
+ return 0;
+ major = strtol(cp, &endptr, 10);
+ if (cp == endptr || *endptr != '.')
+ return 0;
+ cp = endptr + 1;
+ minor = strtol(cp, &endptr, 10);
+ if (cp == endptr || *endptr != '.')
+ return 0;
+ cp = endptr + 1;
+ rev = strtol(cp, &endptr, 10);
+ if (cp == endptr)
+ return 0;
+ return KERNEL_VERSION(major, minor, rev);
+}
+
+int ext2fs_is_before_linux_ver(unsigned int major, unsigned int minor,
+ unsigned int rev)
+{
+ struct utsname ut;
+ static int linux_version_code = -1;
+
+ if (uname(&ut)) {
+ perror("uname");
+ exit(1);
+ }
+ if (linux_version_code < 0)
+ linux_version_code = parse_version_number(ut.release);
+ if (linux_version_code == 0)
+ return 0;
+
+ return linux_version_code < KERNEL_VERSION(major, minor, rev);
+}
+#else
+int ext2fs_is_before_linux_ver(unsigned int major, unsigned int minor,
+ unsigned int rev)
+{
+ return 0;
+}
+#endif
+
/*
* Find a reasonable journal file size (in blocks) given the number of blocks
* in the filesystem. For very small filesystems, it is not reasonable to
{
if (num_blocks < 2048)
return -1;
- if (num_blocks < 32768) /* 128 MB */
+ if (num_blocks <= 8192) /* 32 MB */
return (1024); /* 4 MB */
+ if (num_blocks < 32768) /* 128 MB */
+ return (2048); /* 8 MB */
if (num_blocks < 256*1024) /* 1 GB */
return (4096); /* 16 MB */
if (num_blocks < 512*1024) /* 2 GB */
return 262144; /* 1 GB */
}
+errcode_t ext2fs_get_journal_params(struct ext2fs_journal_params *params,
+ ext2_filsys fs)
+{
+ blk_t total_blks;
+ int ret;
+
+ memset(params, 0, sizeof(*params));
+ if (ext2fs_has_feature_journal_dev(fs->super)) {
+ total_blks = ext2fs_blocks_count(fs->super);
+ if (total_blks < JBD2_MIN_JOURNAL_BLOCKS)
+ return EXT2_ET_JOURNAL_TOO_SMALL;
+
+ if (!ext2fs_has_feature_fast_commit(fs->super)) {
+ params->num_journal_blocks = total_blks;
+ params->num_fc_blocks = 0;
+ return 0;
+ }
+ params->num_journal_blocks = ext2fs_blocks_count(fs->super) *
+ EXT2_JOURNAL_TO_FC_BLKS_RATIO /
+ (EXT2_JOURNAL_TO_FC_BLKS_RATIO + 1);
+ if (JBD2_MIN_JOURNAL_BLOCKS > params->num_journal_blocks)
+ params->num_journal_blocks = JBD2_MIN_JOURNAL_BLOCKS;
+ params->num_fc_blocks = total_blks - params->num_journal_blocks;
+ return 0;
+ }
+
+ ret = ext2fs_default_journal_size(ext2fs_blocks_count(fs->super));
+ if (ret < 0)
+ return EXT2_ET_JOURNAL_TOO_SMALL;
+
+ params->num_journal_blocks = ret;
+ if (ext2fs_has_feature_fast_commit(fs->super))
+ params->num_fc_blocks = params->num_journal_blocks /
+ EXT2_JOURNAL_TO_FC_BLKS_RATIO;
+ return 0;
+}
+
int ext2fs_journal_sb_start(int blocksize)
{
if (blocksize == EXT2_MIN_BLOCK_SIZE)
* POSIX routines if the filesystem is mounted, or using direct I/O
* functions if it is not.
*/
-errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks,
+errcode_t ext2fs_add_journal_inode3(ext2_filsys fs, struct ext2fs_journal_params *jparams,
blk64_t goal, int flags)
{
errcode_t retval;
retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
close(fd);
if (retval)
- return retval;
+ return errno;
}
#endif
#endif
* filesystems is extremely rare these days... Ignore it. */
flags &= ~EXT2_MKJOURNAL_LAZYINIT;
- if ((retval = write_journal_file(fs, jfile, num_blocks, flags)))
+ if ((retval = write_journal_file(fs, jfile, jparams, flags)))
goto errout;
/* Get inode number of the journal file */
}
journal_ino = EXT2_JOURNAL_INO;
if ((retval = write_journal_inode(fs, journal_ino,
- num_blocks, goal, flags)))
+ jparams, goal, flags)))
return retval;
}
return retval;
}
+errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks,
+ blk64_t goal, int flags)
+{
+ struct ext2fs_journal_params jparams;
+ errcode_t ret;
+
+ jparams.num_journal_blocks = num_blocks;
+ jparams.num_fc_blocks = 0;
+
+ return ext2fs_add_journal_inode3(fs, &jparams, goal, flags);
+}
+
errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
{
return ext2fs_add_journal_inode2(fs, num_blocks, ~0ULL, flags);