diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..7564fcc --- /dev/null +++ b/Kconfig @@ -0,0 +1,95 @@ +config SNAPSHOT_ON_DISK + bool "Snapshots on-disk format changes" + help + Snapshots uses a few reserved fields and flags in the Ext2 super block, + and inode structs. + Snapshots require the compatible feature 'exclude_bitmap', meaning that + the exclude bitmap was allocated. + snapshots require the read-only compatible features 'has_snapshot', + meaning that the file system may contain snapshots. + +config SNAPSHOT_BIG_JOURNAL + bool "Create a big journal for COW operations" + help + COW transactions reserve up to 24 times more credits than traditional + transactions for the same operation. On mke2fs and tune2fs, if the + '-J big' option is used to create a journal, increase the default + journal size by a factor of 24. + +config SNAPSHOT_HAS_SNAPSHOT + bool "Avoid offline modifications to a file system with snapshots" + help + Snapshots require the read-only compatible feature 'has_snapshot', + so the file system will be mounted by old kernels in read-only mode + to protect the snapshots. + Fsck displays a warning about possible corruption of the snapshots + in interactive mode and avoids freeing blocks in preen mode. + +config SNAPSHOT_EXCLUDE_BITMAP + bool "Create/check the exclude bitmap" + help + Excluding snapshot blocks from COW is done by setting their bit in + the exclude bitmap. There is one exclude bitmap block per block group. + Mke2fs allocates the exclude bitmap and it cannot be removed. + Fsck checks that all (and only) snapshot file blocks are excluded. + +config SNAPSHOT_CTL + bool "Snapshot control with chsnap/lssnap" + help + Set/clear snapshots parent directory with chattr +/-x. + Take/delete snapshot with chsnap +/-S. + Enable/disable snapshot with chsnap +/-n. + View snapshot status with lssnap. + +config SNAPSHOT_CHECK_LIST + bool "Check snapshot list on fsck" + help + Check that all inodes on snapshot list are valid snapshots and + prompt to terminate list when bad inode is found. + +config SNAPSHOT_FIX_SNAPSHOT + bool "Delete all snapshots on fsck -x" + help + On fsck -x or if the 'fix_snapshot' flag is set, prompt to delete + all snapshot inodes and reset exclude bitmap. + +config SNAPSHOT_HUGE_SNAPSHOT + bool "Map maximum filesystem size with a single snapshot file" + help + To map 2^32 logical blocks, 4 triple indirect blocks are used instead + of just one. The extra 3 triple indirect blocks are stored in-place + of direct blocks, which are not in use by snapshot files. + Snapshots cannot be enabled on filesytem with block size < 4K. + +config SNAPSHOT_MESSAGE_BUFFER + bool "Dump message buffer on fsck" + help + Error messages are recorded in a message buffer after the + journal super block. On journal recovery, the journal message buffer + is copied to the file system message buffer. On fsck, if the message + buffer is not empty, the recorded messages are printed to stdout and + the buffer is cleared. + With a default block size of 4K, there is always 2K of free + space for the message buffer after the 1K super block. + +config SNAPSHOT_CLEANUP + bool "Cleanup snapshot list when removing has_snapshot feature" + help + Discard all snapshots by 'tune2fs -O ^has_snapshot'. + Snapshot inodes are chained on a list starting at the super block. + Delete all snapshot inodes on the list and reset exclude bitmap. + +config SNAPSHOT_CTL_OLD + bool "Old next3 snapshot control with chattr/lsattr -X" + help + Take/delete snapshot with chattr -X +/-S. + Enable/disable snapshot with chattr -X +/-n. + View snapshot status with lsattr -X. + +config SNAPSHOT_EXCLUDE_INODE + bool "Migrate old exclude_inode feature to exclude_bitmap feature" + help + Read exclude bitmap block locations from exclude inode, store them + in the group descriptors, remove the exclude inode, clear the + exclude_inode feature and set the exclude_bitmap feature. + diff --git a/contrib/e4snapshot b/contrib/e4snapshot new file mode 100755 index 0000000..b00f6c3 --- /dev/null +++ b/contrib/e4snapshot @@ -0,0 +1,768 @@ +#!/bin/sh +# +# NEXT3(R) snapshot management script +# +# Written by Amir Goldstein <amir73il@users.sf.net>, 2008 +# Copyright (C) 2008-2011 CTERA Networks +# + +SCRIPT_NAME=$(basename $0) +SCRIPT_VER=1.0.13-7 +# script name is $FSTYPE or e4snapshot or snapshot.$FSTYPE +FSTYPE=$(echo $SCRIPT_NAME | ( IFS=$IFS. ; read a b ; if [ $a = "snapshot" ] ; then echo $b ; else echo $a ; fi ) ) +if [ -z $FSTYPE ] || [ $FSTYPE = "e4snapshot" ] ; then + # deafult to ext4 + FSTYPE=ext4 +fi +SCRIPT_DESC="$FSTYPE snapshot management script" + +# generic e2fs progs +FSCK=e2fsck +MKFS=mke2fs +TUNEFS=tune2fs +LSSNAP="lssnap" +CHSNAP="chsnap" +CHATTR=chattr + +# override generic e2fs progs with installed e2fs.$FSTYPE progs in /sbin fodler +# and override those with private built e2fs.$FSTYPE progs in ./bin folder +for dir in /sbin ./bin ; do +if [ -f $dir/fsck.$FSTYPE ] ; then + FSCK=$dir/fsck.$FSTYPE +fi +if [ -f $dir/mkfs.$FSTYPE ] ; then + MKFS=$dir/mkfs.$FSTYPE +fi +if [ -f $dir/tunefs.$FSTYPE ] ; then + TUNEFS=$dir/tunefs.$FSTYPE +fi +if [ -f $dir/lssnap ] ; then + LSSNAP=$dir/lssnap +elif [ -f $dir/lsattr.$FSTYPE ] ; then + LSSNAP="$dir/lsattr.$FSTYPE -X" +fi +if [ -f $dir/chsnap ] ; then + CHSNAP=$dir/chsnap +elif [ -f $dir/chattr.$FSTYPE ] ; then + CHSNAP="$dir/chattr.$FSTYPE -X" +fi +if [ -f $dir/chattr.$FSTYPE ] ; then + CHATTR=$dir/chattr.$FSTYPE +fi +done + +if [ $# -ge 2 ] && [ $1 != help ] && [ $1_$2 != $1_help ] && [ $1 != config ] && \ + [ $1 != version ] && [ $1 != debug ] && [ $1 != delay ] && [ _$1 != _test ] && [ $1 != tests ] ; then + # parse [[$FSTYPE-mount@]snapshot-name] or $FSTYPE-mount or $FSTYPE-device argument + s=$( echo $2 | ( IFS=$IFS@ ; read a b ; echo $b ) ) + if [ -z $s ] ; then + # check if argument is a $FSTYPE mount point + dev=$( cat /proc/mounts | while read a b c d ; do ( [ _$b = _$2 ] && [ _$c = _$FSTYPE ] && echo $a ) ; done ) + if [ ! -z $dev ] ; then + # found $FSTYPE mount point + ROOTMNT=$2 + ROOTDEV=$dev + elif [ -b $2 ] || [ $1 = mkfs ] || [ $1 = on ] || [ $1 = off ] ; then + # argument is a $FSTYPE block device + ROOTDEV=$2 + ROOTMNT=$3 + else + # snapshot name was given without $FSTYPE mount point + s=$2 + fi + else + # snapshot name is prepended with $FSTYPE mount point + ROOTMNT=$( echo $2 | ( IFS=$IFS@ ; read a b ; echo $a ) ) + # find $FSTYPE block device from $FSTYPE mount point + ROOTDEV=$( cat /proc/mounts | while read a b c d ; do ( [ _$b = _$ROOTMNT ] && [ _$c = _$FSTYPE ] && echo $a ) ; done ) + if [ -z $ROOTDEV ] ; then + echo "$0 $1 $2: $ROOTMNT is not a mounted $FSTYPE filesystem!" + exit 1 + fi + # strip / from snapshot name + s=$( echo $s | ( IFS=$IFS/ ; read a b ; echo $a ) ) + fi +fi + +SYSDEBUGDIR=/sys/kernel/debug +SNAPDEBUGDIR=$SYSDEBUGDIR/$FSTYPE + +# default debug-level is old-debug-level +if [ ! -d $SNAPDEBUGDIR ] ; then + mount -t debugfs debugfs $SYSDEBUGDIR 2> /dev/null +fi +if [ -e $SNAPDEBUGDIR/snapshot-debug ] ; then + L=$(cat $SNAPDEBUGDIR/snapshot-debug 2> /dev/null) + if [ -z $2 ] ; then + l=$L + else + l=$2 + fi +else + L=0 + l=0 +fi +# check if test read is enabled +if [ -e $SNAPDEBUGDIR/test-read-delay-msec ] ; then + test_read=$(cat $SNAPDEBUGDIR/test-read-delay-msec) +else + test_read=0 +fi + +# read $FSTYPE block device and mount point from conf file +CONFFILE=.$FSTYPE.conf + +# look for snapshot config file in current then home directory +if [ -z $ROOTDEV ] && [ -z $ROOTMNT ] ; then + if [ -f ./$CONFFILE ] ; then + . ./$CONFFILE + elif [ -f ~/$CONFFILE ] ; then + . ~/$CONFFILE + elif [ $( mount -t $FSTYPE | grep -v noload | wc -l ) = 1 ] ; then + # exactly one mounted $FSTYPE filesystem found (mounted snapshots excluded) + ROOTDEV=$( mount -t $FSTYPE | grep -v noload | ( read a b c d ; echo $a ) ) + ROOTMNT=$( mount -t $FSTYPE | grep -v noload | ( read a b c d ; echo $c ) ) + fi +fi + +if [ ! -z $1 ] && [ $1 != help ] && [ $1_$2 != $1_help ] && [ $1 != config ] && \ + [ $1 != version ] && [ $1 != debug ] && [ $1 != delay ] && [ _$1 != _test ] && [ $1 != tests ] ; then + if [ $1 != mkfs ] && [ $1 != fsck ] && [ $1 != on ] && [ $1 != off ] ; then + if [ -z $ROOTMNT ] ; then + echo "$0: ambigous or missing $FSTYPE mount point" + echo "run '$0 config' to set default value" + exit 1 + fi + if [ ! -d $ROOTMNT ] ; then + echo "$0: bad $FSTYPE mount point $ROOTMNT" + exit 1 + fi + else + if [ -z $ROOTDEV ] ; then + echo "$0: ambigous or missing $FSTYPE block device" + echo "run '$0 config' to set default value" + exit 1 + fi + if [ ! -b $ROOTDEV ] && [ ! -f $ROOTDEV ] ; then + echo "$0: bad $FSTYPE block device $ROOTDEV" + exit 1 + fi + fi + export ROOTMNT + export ROOTDEV + S=$ROOTMNT@$s +fi + +if [ -z $SNAPDIR ] ; then + # directory inside $FSTYPE filesystem to store snapshot files + SNAPDIR=$ROOTMNT/.snapshots +fi +if [ -z $SNAPMNT ] ; then + # directory prefix for snapshot mount points + # snaphot mount points will be created as $SNAPMNT<snapshot-name> + # default to ZFS snapshot naming convention <filesystem>@<snapshot> + SNAPMNT=$ROOTMNT@ +fi +TESTDIR=test + +# See how we were called. +case "$1" in +# Snapshot global commands + help) + if [ -z $2 ] ; then + $0 + else + $0 $2 help + fi + ;; + version) + if [ $1_$2 = $1_help ] ; then + echo "$1: display $FSTYPE snapshot version." + exit 0 + fi + ver=$(cat $SNAPDEBUGDIR/snapshot-version) + echo snapshot-version = $ver + ;; + debug) + if [ $1_$2 = $1_help ] ; then + echo "$1: set snapshot debug level." + echo "debug levels: 0=none, 1=error, 2=warning, 3=info, 4=debug." + echo "usage: $SCRIPT_NAME $1 [debug-level]" + exit 0 + fi + if [ -e $SNAPDEBUGDIR/snapshot-debug ] ; then + echo $l > $SNAPDEBUGDIR/snapshot-debug + l=$(cat $SNAPDEBUGDIR/snapshot-debug) + fi + echo snapshot-debug = $l + ;; + delay) + if [ $1_$2 = $1_help ] ; then + echo "$1: set snapshot test delay." + echo "adds delay to specific snapshot operation." + echo "usage: $SCRIPT_NAME $1 <take|delete|cow|read|bitmap> [delay-sec] [delay-msec]" + exit 0 + fi + if [ -z $2 ] ; then + echo missing test name + $0 usage + fi + if [ ! -e $SNAPDEBUGDIR/test-$2-delay-msec ] ; then + echo invalid test name $2 + $0 usage + fi + if [ ! -z $3 ] ; then + if [ $3 -gt 60 ] || [ $3 -lt 0 ] ; then + echo valid range for test delay is 0-60 sec + else + if [ ! -z $4 ] ; then + ms=$3$4 + else + ms=${3}000 + fi + echo $ms > $SNAPDEBUGDIR/test-$2-delay-msec + fi + fi + ms=$(cat $SNAPDEBUGDIR/test-$2-delay-msec) + echo test-$2-delay = $ms msec + ;; + +# $FSTYPE filesystem commands + config) + if [ $1_$2 = $1_help ] ; then + echo "$1: configure or print default $FSTYPE filesystem parameters." + echo "creates configuration file $CONFFILE in current directory." + echo "usage: $SCRIPT_NAME config [$FSTYPE-device $FSTYPE-mount]" + exit 0 + fi + if [ ! -z $2 ] && [ ! -b $2 ] && [ ! -f $2 ] ; then + echo "$0 config: bad block device: $2" + exit 1 + fi + if [ ! -z $3 ] && [ ! -d $3 ] ; then + echo "$0 config: bad mount point: $3" + exit 1 + fi + if [ ! -z $2 ] && [ ! -z $3 ] ; then + echo ROOTDEV=$2 > $CONFFILE + echo ROOTMNT=$3 >> $CONFFILE + fi + test -f $CONFFILE && cat $CONFFILE + ;; + on) + if [ $1_$2 = $1_help ] ; then + echo "on: add the snapshot feature to an existing ext3 filesystem." + echo "after adding the snapshot feature to an ext3 filesystem, the filesystem" + echo "can no longer be mounted as ext3 read-write (only as $FSTYPE or ext3 read-only)." + echo "usage: $SCRIPT_NAME on [$FSTYPE-device]" + exit 0 + fi + $TUNEFS -e remount-ro -O has_snapshot $ROOTDEV || exit 1 + echo snapshot feature added to $ROOTDEV + echo . + ;; + off) + if [ $1_$2 = $1_help ] ; then + echo "off: remove the snapshot feature from a $FSTYPE filesystem." + echo "after removing the snapshot feature, all existing snapshots will be discarded" + echo "and the filesystem could be mounted as ext3 read-write again." + echo "usage: $SCRIPT_NAME off [$FSTYPE-device]" + exit 0 + fi + $TUNEFS -O ^has_snapshot $ROOTDEV || exit 1 + echo snapshot feature removed from $ROOTDEV + echo . + ;; + mkfs) + if [ $1_$2 = $1_help ] ; then + echo "$1: create a $FSTYPE filesystem." + echo "usage: $SCRIPT_NAME mkfs [$FSTYPE-device]" + exit 0 + fi + $MKFS -b 4096 -j -J big -O has_snapshot,exclude_inode $ROOTDEV || exit 1 + $TUNEFS -e remount-ro $ROOTDEV + echo $FSTYPE filesystem created on $ROOTDEV + echo . + ;; + + stat) + if [ $1_$2 = $1_help ] ; then + echo "stat: display status of $FSTYPE filesystem and all its snapshots." + echo "snapshot attributes are displayed in short format. to display" + echo "attributes in long format use the '$0 lsattr' command." + echo "usage: $SCRIPT_NAME stat [$FSTYPE-mount]" + exit 0 + fi + echo "Mounted $FSTYPE filesystem and snapshots:" + df -h $ROOTMNT $SNAPMNT* 2> /dev/null + echo . + echo "Snapshots list:" + echo "id inode attributes disk-usage mtime filename" + echo "---------------------------------------------" + for s in $( ls -vr $SNAPDIR/* ) ; do + ( echo "$( $LSSNAP -v $s ) " ; \ + echo "$( du -h $s ) " ; \ + echo "$( ls -lih --color=never $s ) " ) | \ + ( read id attr a ; read du b ; read ino c d e f g mm dd tt fname h ; \ + echo $id $ino $attr $du $mm $dd $tt $fname ) + done 2> /dev/null + echo . + ;; + lsattr) + if [ $1_$2 = $1_help ] ; then + echo "$1: display attributes of $FSTYPE snapshots in long format." + echo "usage: $SCRIPT_NAME $1" + exit 0 + fi + $LSSNAP -l $SNAPDIR + ;; + +# Snapshot control commands (single snapshot) + take) + if [ $1_$2 = $1_help ] ; then + echo "take: take a snapshot." + echo "usage: $SCRIPT_NAME take [[$FSTYPE-mount@]snapshot-name] (default=$FSTYPE-mount@GMT-date)" + exit 0 + fi + mkdir -p $SNAPDIR || exit 1 + $CHATTR +dx $SNAPDIR || exit 1 + if [ -z $s ] ; then + # default snapshot-name is the <date-time> in shadow copy format + s=$( date -u +GMT-%Y.%m.%d-%H.%M.%S ) + S=$ROOTMNT@$s + fi + if [ -f $SNAPDIR/$s ] ; then + echo "$0 take: snapshot $S already exists!" + exit 1 + fi + touch $SNAPDIR/$s 2> /dev/null + sync + $CHSNAP +S $SNAPDIR/$s || exit 1 + echo snapshot $S was created + echo . + ;; + delete) + if [ $1_$2 = $1_help ] ; then + echo "delete: umount a snapshot and mark it for deletion." + echo "any non-mounted snapshot can be marked for deletion" + echo "but some snapshot deletion is deferred to later time." + echo "usage: $SCRIPT_NAME delete [$FSTYPE-mount@]<snapshot-name>" + exit 0 + fi + if [ -z $s ] ; then + $0 + exit 1 + fi + if [ -f $SNAPDIR/$s ] ; then + $0 umount $s + $CHSNAP -S $SNAPDIR/$s || exit 1 + fi + echo snapshot $S is deleted + echo . + ;; + remove) + if [ $1_$2 = $1_help ] ; then + echo "remove: delete a snapshot permanently." + echo "this command will fail for active snapshot and snapshots" + echo "in use by older snapshots. try using the delete command." + echo "usage: $SCRIPT_NAME remove [$FSTYPE-mount@]<snapshot-name>" + exit 0 + fi + if [ -z $2 ] ; then + $0 + exit 1 + fi + if [ -f $SNAPDIR/$s ] ; then + $0 delete $s + rm -f $SNAPDIR/$s 2> /dev/null || exit 1 + fi + echo snapshot $S was removed + echo . + ;; + enable) + if [ $1_$2 = $1_help ] ; then + echo "enable: enable access to snapshot file." + echo "usage: $SCRIPT_NAME enable [$FSTYPE-mount@]<snapshot-name>" + exit 0 + fi + if [ -z $2 ] ; then + echo "$0 enable: snapshot name is expected!" + exit 1 + fi + if [ ! -f $SNAPDIR/$s ] ; then + echo "$0 enable: snapshot $S not found!" + exit 1 + fi + $CHSNAP +n $SNAPDIR/$s || exit 1 + ;; + disable) + if [ $1_$2 = $1_help ] ; then + echo "disable: disable access to snapshot file." + echo "usage: $SCRIPT_NAME disable [$FSTYPE-mount@]<snapshot-name>" + exit 0 + fi + if [ -z $2 ] ; then + echo "$0 disable: snapshot name is expected!" + exit 1 + fi + if [ ! -f $SNAPDIR/$s ] ; then + echo "$0 disable: snapshot $S not found!" + exit 1 + fi + if grep $SNAPMNT$s /proc/mounts ; then + echo "$0 disable: snapshot $S is mounted!" + exit 1 + fi + $CHSNAP -n $SNAPDIR/$s || exit 1 + ;; + +# Snapshot control commands (single or all snapshots) + mount) + if [ $1_$2 = $1_help ] ; then + echo "mount: mount a snapshot." + echo "usage: $SCRIPT_NAME mount [$FSTYPE-device $FSTYPE-mount]" + echo "usage: $SCRIPT_NAME mount [$FSTYPE-mount@]<snapshot-name>" + exit 0 + fi + if [ -z $s ] ; then + # mount $FSTYPE filesystem + MNTOPT="-o errors=remount-ro" + # uncomment to enable delayed allocation + #MNTOPT="$MNTOPT,nodelalloc" + if [ ! -z $ROOTDEV ] && [ -f $ROOTDEV ] ; then + MNTOPT="$MNTOPT,loop,noinit_itable" + fi + # we may be called from mount.$FSTYPE so add -i + mount -t $FSTYPE -i $MNTOPT $ROOTDEV $ROOTMNT || exit 1 + echo $FSTYPE filesystem is mounted on $ROOTMNT + else + $0 enable $s || exit 1 + if grep $SNAPMNT$s /proc/mounts ; then + echo "$0 mount: snapshot $S is already mounted!" + exit 1 + fi + mkdir -p $SNAPMNT$s + mount -t $FSTYPE -r -o loop,noload $SNAPDIR/$s $SNAPMNT$s || exit 1 + echo snapshot $S is mounted + fi + echo . + ;; + umount) + if [ $1_$2 = $1_help ] ; then + echo "umount: umount a snapshot." + echo "usage: $SCRIPT_NAME umount [$FSTYPE-mount]" + echo "usage: $SCRIPT_NAME umount [$FSTYPE-mount@]<snapshot-name>" + exit 0 + fi + if [ -z $s ] ; then + # umount all snapshots + for s in $( ls $SNAPDIR/ 2> /dev/null ) ; do + $0 umount $s 2> /dev/null + done + # umount $FSTYPE filesystem + # we may be called from umount.$FSTYPE so add -i + umount -i $MNTOPT $ROOTMNT || exit 1 + echo $FSTYPE filesystem was unmounted + else + if [ -d $SNAPMNT$s ] ; then + umount $SNAPMNT$s || exit 1 + rmdir $SNAPMNT$s + fi + $0 disable $s + echo snapshot $S was unmounted + fi + echo . + ;; + +# Snapshot sanity tests + dump) + if [ $1_$2 = $1_help ] ; then + echo "dump: print a map of a snapshot file to kernel ring buffer." + echo "usage: $SCRIPT_NAME dump [$FSTYPE-mount@]<snapshot-name>" + exit 0 + fi + if [ -z $2 ] ; then + echo "$0 dump: snapshot name is expected!" + exit 1 + fi + if [ ! -f $SNAPDIR/$s ] ; then + echo "$0 dump: snapshot $S not found!" + exit 1 + fi + $CHATTR -d $SNAPDIR/$s + echo "map of snapshot $S was printed to kernel ring buffer." + echo "use dmesg to display kernel ring buffer." + echo . + ;; + fsck) + if [ $1_$2 = $1_help ] ; then + echo "fsck: run fsck on a snapshot or $FSTYPE filesystem." + echo "usage: $SCRIPT_NAME fsck [$FSTYPE-device]" + echo "usage: $SCRIPT_NAME fsck [$FSTYPE-mount@]<snapshot-name>" + exit 0 + fi + if [ -z $s ] ; then + echo "Fscking $FSTYPE filesystem on $ROOTDEV..." + $FSCK -pf -C 0 $ROOTDEV >&2 && \ + echo $FSTYPE filesystem on $ROOTDEV 'is healthy' || \ + echo $FSTYPE filesystem on $ROOTDEV 'has errors' + else + $0 enable $s || exit 1 + echo "Fscking snapshot $S..." + $FSCK -nf -C 0 $SNAPDIR/$s >&2 && \ + echo snapshot $S 'is healthy' || \ + echo snaphsot $S 'has errors' + if ! grep $SNAPMNT$s /proc/mounts ; then + $0 disable $s || exit 1 + fi + fi + echo . + ;; + + mktest) + if [ -d $ROOTMNT/A ] ; then + exit 0 + fi + mkdir $ROOTMNT/A + mkdir $ROOTMNT/B + mkdir $ROOTMNT/C + mkdir $ROOTMNT/D + mkdir $ROOTMNT/E + echo aligator > $ROOTMNT/A/a.txt + echo bizon > $ROOTMNT/B/b.txt + echo camel > $ROOTMNT/C/c.txt + # create non-snapshot dir in snapshots dir block group + mkdir -p $ROOTMNT/$TESTDIR + $CHATTR -x $ROOTMNT/$TESTDIR || exit 1 + ;; + rmtest) + rm -rf $ROOTMNT/? 2> /dev/null + rm -rf $ROOTMNT/$TESTDIR 2> /dev/null + ;; + lstest) + if [ $1_$2 = $1_help ] ; then + echo "lstest: list the content of test files in a snapshot or $FSTYPE filesystem." + echo "usage: $SCRIPT_NAME lstest [[$FSTYPE-mount@]snapshot-name] (default=$FSTYPE-mount)" + exit 0 + fi + if [ -z $s ] ; then + echo Files in file system: + d=$ROOTMNT + else + $0 mount $s || exit 1 + echo Files in snapshot $s: + d=$SNAPMNT$s + fi + if [ -d $d ] ; then + cd $d > /dev/null + grep -v xxx ?/* + test -e $TESTDIR/md5list && (cd $TESTDIR ; md5sum -c md5list || exit 1) + #find $d/ -maxdepth 1 -type f -print + cd - > /dev/null + echo . + fi + if [ ! -z $s ] ; then + $0 umount $s || exit 1 + fi + ;; + tests) + if [ $1_$2 = $1_help ] ; then + echo "tests: run snapshot sanity tests 1..N." + echo "usage: $SCRIPT_NAME tests [test-number] [delay-sec] [file-size-mb]" + echo "delay-sec: sleep between tests" + echo "file-size-mb: test file size in mega bytes (default = 1)" + exit 0 + fi + if [ -z $2 ] ; then + N=4 + else + N=$2 + fi + # disable read-ahead if test read is enabled + test $test_read = 0 || blockdev --setra 0 $ROOTDEV + for s in $( ls $SNAPDIR/ 2> /dev/null ) ; do + $0 delete $s 2> /dev/null + done + for s in $( ls $SNAPDIR/ 2> /dev/null ) ; do + $0 remove $s 2> /dev/null + done + for n in $( seq 0 $N ) ; do + $0 test $n $3 $4 || exit 1 + done + $0 lstest + for n in $( seq 1 $N ) ; do + $0 lstest $n + done + # skip fsck if non zero read delay or zero delay between tests + ( [ ${test_read}_ms = 0_ms ] && [ ${3}_ms != 0_ms ] ) || exit 0 + sleep 1 + if [ $N = 0 ] ; then + $0 umount $ROOTMNT || exit 1 + $0 fsck $ROOTDEV + $0 mount $ROOTDEV $ROOTMNT || exit 1 + else + for n in $( seq 1 $N ) ; do + $0 fsck $n 2> /dev/null + done + fi + ;; + test) + if [ $1_$2 = $1_help ] ; then + echo "test: run snapshot sanity test N." + echo "usage: $SCRIPT_NAME test [test-number] [delay-sec] [file-size-mb]" + echo "delay-sec: sleep before test" + echo "file-size-mb: test file size in mega bytes (default = 1)" + exit 0 + fi + if [ -z $2 ] ; then + n=1 + else + n=$2 + fi + if [ $n = 0 ] ; then + $0 rmtest + exit 0 + fi + $0 mktest + echo + echo Running snapshot test $n: + echo ------------------------ + if [ ! -z $3 ] ; then + sleep $3 # delay between tests + fi + if [ ! -z $4 ] ; then + M=$4 + else + M=1 + fi + F=${M}M + cd $ROOTMNT/$TESTDIR > /dev/null + NOTRUNC="conv=notrunc" + # uncomment the following line to run in-place write tests + INPLACE=$NOTRUNC + # uncomment the following line to run direct I/O write tests + DIRECT="oflag=direct" + TRUNCSIZE=4 + echo Appending $F zeros to $F.1 $DIRECT + # append writes to new allocated blocks + dd if=/dev/zero bs=1M count=$M of=$F.1 $NOTRUNC oflag=append $DIRECT status=noxfer || exit 1 + echo Writing $F random data to $n files + for i in $( seq 1 $n ) ; do + # 1st rewrite moves existing blocks to snapshot and allocates new blocks + fallocate -mrs -l 1M $F.$i 2>/dev/null || \ + dd if=/dev/urandom bs=1M count=$M of=$F.$i $INPLACE status=noxfer || exit 1 + # subsequent rewrites doesn't move blocks to snapshot + fallocate -mrs -l 1M $F.1 2>/dev/null || \ + dd if=/dev/urandom bs=1M count=$M of=$F.1 $INPLACE $DIRECT status=noxfer || exit 1 + done + for i in $( seq 1 $n ) ; do + md5sum $F.$i || exit 1 + done > md5list + cd - > /dev/null + $0 lstest || exit 1 + s=$n + $0 take $s || exit 1 + case "$n" in + 1) + echo Create test: + echo ------------ + echo 'Creating d.txt' + echo dodo > $ROOTMNT/D/d.txt + echo 'Creating e.txt' + echo emu > $ROOTMNT/E/e.txt + ;; + 2) + echo Write test: + echo ----------- + echo 'Writing b.txt (append)' + echo 'barracuda' >> $ROOTMNT/B/b.txt + echo 'Writing c.txt (truncate)' + echo 'crocodile' > $ROOTMNT/C/c.txt + ;; + 3) + echo Remove test: + echo ------------ + echo "Truncating c.txt (to size $TRUNCSIZE)" + truncate -s $TRUNCSIZE $ROOTMNT/C/c.txt + echo 'Removing d.txt' + rm $ROOTMNT/D/d.txt + ;; + 4) + echo Restore test: + echo ------------- + f=$( ls -v $SNAPDIR/ | head -n 1 ) + echo 'Restoring from snapshot' $f + if ! grep $SNAPMNT$f /proc/mounts ; then + $0 mount $f || exit 1 + fi + rm -rf $ROOTMNT/? + cp -R $SNAPMNT$f/? $ROOTMNT/ + $0 umount $f || exit 1 + ;; + 5) + echo Delete excluded test: + echo --------------------- + #echo Removing excluded files + #rm $ROOTMNT/*M + ;; + 6) + echo Delete reallocated test: + echo ------------------------ + #echo Removing /$F + #rm $ROOTMNT/$F + ;; + 7) + echo Shrink snapshots test: + echo --------------------- + $0 mount 1 + for f in 5 6 4 3 2 ; do + echo 'Deleting snapshot' $f + $0 delete $f 2> /dev/null + $0 stat + done + ;; + 8) + echo Merge snapshots test: + echo --------------------- + $0 umount 1 + for f in 7 ; do + echo 'Deleting snapshot' $f + $0 delete $f 2> /dev/null + $0 stat + done + ;; + esac || exit 1 + echo . + $0 lstest || exit 1 + $0 lstest $s || exit 1 + $0 stat + ;; + *) + echo "$SCRIPT_NAME v$SCRIPT_VER ($SCRIPT_DESC)" + echo "usage: $SCRIPT_NAME help [cmd]" + echo "usage: $SCRIPT_NAME version" + echo + echo "$FSTYPE filesystem maintenance commands:" + echo "usage: $SCRIPT_NAME {config|mount} [$FSTYPE-device $FSTYPE-mount]" + echo "usage: $SCRIPT_NAME {mkfs|fsck|on|off} [$FSTYPE-device]" + echo "usage: $SCRIPT_NAME {stat|umount} [$FSTYPE-mount]" + echo + echo "$FSTYPE snapshot commands:" + echo "usage: $SCRIPT_NAME take [[$FSTYPE-mount@]snapshot-name] (default=$FSTYPE-mount@GMT-date)" + echo "usage: $SCRIPT_NAME {mount|umount|delete|remove|fsck|dump} [$FSTYPE-mount@]<snapshot-name>" + echo + echo "<snapshot-name> parameter may be just the name of the snapshot or may be" + echo "given in ZFS style of <$FSTYPE-mount>@<snapshot-name>." + echo "in the former case, the $FSTYPE mount point is either read from /proc/mounts" + echo "(if only one $FSTYPE filesystem is mounted) or from $CONFFILE config file." + echo "in the latter case, the $FSTYPE mount point is parsed from the snapshot name prefix." + echo + echo "$FSTYPE debug and test commands:" + echo "usage: $SCRIPT_NAME debug [debug-level]" + echo "usage: $SCRIPT_NAME delay <take|delete|cow|read|bitmap> [delay-sec] [delay-msec]" + echo "usage: $SCRIPT_NAME {test|tests} [test-number] [delay-sec] [file-size-mb]" + echo + exit 1 +esac + +exit 0 diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in index 91f3ddf..b187db0 100644 --- a/e2fsck/Makefile.in +++ b/e2fsck/Makefile.in @@ -225,6 +225,26 @@ install: $(PROGS) $(MANPAGES) $(FMANPAGES) installdirs (cd $(DESTDIR)$(man8dir); \ $(LN) $(LINK_INSTALL_FLAGS) e2fsck.8 fsck.$$i.8); \ done + $(Q) if test -f $(DESTDIR)$(root_sysconfdir)/e2fsck.conf; then \ + if cmp -s $(DESTDIR)$(root_sysconfdir)/e2fsck.conf \ + $(srcdir)/e2fsck.conf; then \ + true; \ + else \ + echo " INSTALL_DATA $(root_sysconfdir)/e2fsck.conf"; \ + mv $(DESTDIR)$(root_sysconfdir)/e2fsck.conf \ + $(DESTDIR)$(root_sysconfdir)/e2fsck.conf.e2fsprogs-old; \ + $(INSTALL_DATA) $(srcdir)/e2fsck.conf \ + $(DESTDIR)$(root_sysconfdir)/e2fsck.conf; \ + echo "Your e2fsck.conf is too old. Backing up old version in"; \ + echo "$(DESTDIR)$(root_sysconfdir)/e2fsck.conf.e2fsprogs-old. Please check to see"; \ + echo "if you have any local customizations that you wish to preserve."; \ + echo " "; \ + fi; \ + else \ + echo " INSTALL_DATA $(root_sysconfdir)/e2fsck.conf"; \ + $(INSTALL_DATA) $(srcdir)/e2fsck.conf \ + $(DESTDIR)$(root_sysconfdir)/e2fsck.conf; \ + fi install-strip: install $(Q) for i in $(PROGS); do \ @@ -250,6 +270,9 @@ uninstall: $(DESTDIR)$(root_sbindir)/fsck.ext3 \ $(DESTDIR)$(root_sbindir)/fsck.ext4 \ $(DESTDIR)$(root_sbindir)/fsck.ext4dev + if cmp -s $(srcdir)/e2fsck.conf $(DESTDIR)/$(root_sysconfdir)/e2fsck.conf; then \ + $(RM) $(DESTDIR)/$(root_sysconfdir)/e2fsck.conf; \ + fi clean: $(RM) -f $(PROGS) \#* *\# *.s *.o *.a *~ core e2fsck.static \ diff --git a/e2fsck/e2fsck.8.in b/e2fsck/e2fsck.8.in index c554717..326d315 100644 --- a/e2fsck/e2fsck.8.in +++ b/e2fsck/e2fsck.8.in @@ -8,7 +8,7 @@ e2fsck \- check a Linux ext2/ext3/ext4 file system .SH SYNOPSIS .B e2fsck [ -.B \-pacnyrdfkvtDFV +.B \-pacnyrdfkvtxDFV ] [ .B \-b @@ -193,7 +193,6 @@ or repairs. .BI fragcheck During pass 1, print a detailed report of any discontiguous blocks for files in the filesystem. -.RE .TP .B \-f Force checking even if the file system seems clean. @@ -297,6 +296,16 @@ may not be specified at the same time as the or .B \-p options. +.TP +.B \-x +Delete all snapshots before repairing the filesystem. Unless +.B e2fsck +is run in preen mode, it may modify the filesystem in a way that will +corrupt ext4 snapshots. So when ext4 snapshots exists and the filesystem +could not be repaired in preen mode, using this option is recommended. +This option may not be specified at the same time as the +.B \-n +option. .SH EXIT CODE The exit code returned by .B e2fsck diff --git a/e2fsck/e2fsck.conf b/e2fsck/e2fsck.conf new file mode 100644 index 0000000..8ce70d1 --- /dev/null +++ b/e2fsck/e2fsck.conf @@ -0,0 +1,2 @@ +[options] + clear_test_fs_flag = false diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index 4e7fc72..b73ab1a 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -156,6 +156,9 @@ struct resource_track { #define E2F_OPT_COMPRESS_DIRS 0x0400 #define E2F_OPT_FRAGCHECK 0x0800 #define E2F_OPT_JOURNAL_ONLY 0x1000 /* only replay the journal */ +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT +#define E2F_OPT_CLEAR_SNAPSHOTS 0x2000 +#endif /* * E2fsck flags @@ -178,6 +181,12 @@ struct resource_track { #define E2F_FLAG_GOT_DEVSIZE 0x0800 /* Device size has been fetched */ #define E2F_FLAG_EXITING 0x1000 /* E2fsck exiting due to errors */ #define E2F_FLAG_TIME_INSANE 0x2000 /* Time is insane */ +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +#define E2F_FLAG_EXCLUDE_INODE 0x4000 /* Request to recreate exclude inode */ +#endif +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT +#define E2F_FLAG_CLEAR_SNAPSHOTS 0x8000 /* Clear all snapshot inodes */ +#endif #define E2F_RESET_FLAGS (E2F_FLAG_TIME_INSANE) @@ -234,6 +243,9 @@ struct e2fsck_struct { ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/ ext2fs_block_bitmap block_found_map; /* Blocks which are in use */ +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + ext2fs_block_bitmap block_excluded_map; /* Blocks which are excluded */ +#endif ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */ ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */ @@ -476,6 +488,15 @@ void e2fsck_rehash_directories(e2fsck_t ctx); void check_super_block(e2fsck_t ctx); int check_backup_super_block(e2fsck_t ctx); void check_resize_inode(e2fsck_t ctx); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +void check_exclude_inode(e2fsck_t ctx); +#endif +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT +void check_snapshots(e2fsck_t ctx); +#endif +#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER +void e2fsck_clear_message_buffer(e2fsck_t ctx); +#endif /* util.c */ extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size, diff --git a/e2fsck/journal.c b/e2fsck/journal.c index b741eb9..a1eefb4 100644 --- a/e2fsck/journal.c +++ b/e2fsck/journal.c @@ -837,6 +837,24 @@ static errcode_t recover_ext3_journal(e2fsck_t ctx) if (journal->j_superblock->s_errno) { +#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER + /* journal message buffer at journal super block + 1K */ + char *buf = ((char *) journal->j_superblock) + + SUPERBLOCK_OFFSET; + int n, len = ctx->fs->blocksize - MSGBUF_OFFSET; + + if (len >= MSGBUF_OFFSET && *buf) { + /* keep it simple - write in MSGBUF_OFFSET blocksize */ + io_channel_set_blksize(ctx->fs->io, MSGBUF_OFFSET); + n = len / MSGBUF_OFFSET; + /* write journal message buffer to super block + 2K */ + retval = io_channel_write_blk(ctx->fs->io, 1, n, buf); + io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize); + /* clear journal message buffer */ + memset(buf, 0, len); + } + +#endif ctx->fs->super->s_state |= EXT2_ERROR_FS; ext2fs_mark_super_dirty(ctx->fs); journal->j_superblock->s_errno = 0; diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 4bf80d2..f01f579 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -55,6 +55,11 @@ #define _INLINE_ inline #endif +#define ext2fs_file_acl_block(inode) \ + (inode)->i_file_acl +#define ext2fs_fast_mark_block_bitmap2(map, blk) \ + ext2fs_fast_mark_block_bitmap(map, blk) + static int process_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data); @@ -79,13 +84,17 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, struct process_block_struct { ext2_ino_t ino; unsigned is_dir:1, is_reg:1, clear:1, suppress:1, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + fragmented:1, compressed:1, bbcheck:1, snapfile:1; +#else fragmented:1, compressed:1, bbcheck:1; +#endif blk64_t num_blocks; - blk_t max_blocks; + blk64_t max_blocks; e2_blkcnt_t last_block; e2_blkcnt_t last_db_block; int num_illegal_blocks; - blk_t previous_block; + blk64_t previous_block; struct ext2_inode *inode; struct problem_context *pctx; ext2fs_block_bitmap fs_meta_blocks; @@ -626,6 +635,18 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->flags |= E2F_FLAG_ABORT; return; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP) + pctx.errcode = ext2fs_allocate_block_bitmap(fs, + _("excluded block map"), + &ctx->block_excluded_map); + if (pctx.errcode) { + pctx.num = 1; + fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } +#endif e2fsck_setup_tdb_icount(ctx, 0, &ctx->inode_link_info); if (!ctx->inode_link_info) pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0, @@ -891,7 +912,11 @@ void e2fsck_pass1(e2fsck_t ctx) if (ino == EXT2_BOOT_LOADER_INO) { if (LINUX_S_ISDIR(inode->i_mode)) problem = PR_1_RESERVED_BAD_MODE; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + } else if (ino == EXT2_RESIZE_INO || ino == EXT2_EXCLUDE_INO) { +#else } else if (ino == EXT2_RESIZE_INO) { +#endif if (inode->i_mode && !LINUX_S_ISREG(inode->i_mode)) problem = PR_1_RESERVED_BAD_MODE; @@ -1072,7 +1097,10 @@ void e2fsck_pass1(e2fsck_t ctx) (inode->i_block[EXT2_IND_BLOCK] || inode->i_block[EXT2_DIND_BLOCK] || inode->i_block[EXT2_TIND_BLOCK] || - inode->i_file_acl)) { +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + (inode->i_flags & EXT4_SNAPFILE_FL) || +#endif + ext2fs_file_acl_block(inode))) { inodes_to_process[process_inode_count].ino = ino; inodes_to_process[process_inode_count].inode = *inode; process_inode_count++; @@ -1144,6 +1172,27 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->flags &= ~E2F_FLAG_RESIZE_INODE; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + if (ctx->flags & E2F_FLAG_EXCLUDE_INODE) { + ext2fs_block_bitmap save_bmap; + + save_bmap = fs->block_map; + fs->block_map = ctx->block_found_map; + clear_problem_context(&pctx); + pctx.errcode = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); + if (pctx.errcode && + fix_problem(ctx, PR_1_EXCLUDE_INODE_CREATE, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, inode, + "clear_exclude"); + fs->super->s_feature_compat &= ~EXT2_FEATURE_COMPAT_EXCLUDE_INODE; + ctx->flags |= E2F_FLAG_RESTART; + } + fs->block_map = save_bmap; + ctx->flags &= ~E2F_FLAG_EXCLUDE_INODE; + } + +#endif if (ctx->flags & E2F_FLAG_RESTART) { /* * Only the master copy of the superblock and block @@ -1649,6 +1698,19 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode, int restart_flag, const char *source) { +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + /* don't clear inode with blocks when preening volume with active snapshot */ + if ((ctx->fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) && + ctx->fs->super->s_snapshot_inum) { + int i; + for (i = 0; i < EXT2_N_BLOCKS; i++) + if (inode->i_block[i]) + /* if we don't halt, inode blocks will be freed */ + preenhalt(ctx); + } + +#endif inode->i_flags = 0; inode->i_links_count = 0; ext2fs_icount_store(ctx->inode_link_info, ino, 0); @@ -1892,6 +1954,9 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, pb.previous_block = 0; pb.is_dir = LINUX_S_ISDIR(inode->i_mode); pb.is_reg = LINUX_S_ISREG(inode->i_mode); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + pb.snapfile = (pb.is_reg && (inode->i_flags & EXT4_SNAPFILE_FL)); +#endif pb.max_blocks = 1 << (31 - fs->super->s_log_block_size); pb.inode = inode; pb.pctx = pctx; @@ -1899,6 +1964,15 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, pctx->ino = ino; pctx->errcode = 0; +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + if (pb.snapfile && (ctx->flags & E2F_FLAG_CLEAR_SNAPSHOTS)) { + /* discarding all snapshot files */ + e2fsck_clear_inode(ctx, ino, inode, E2F_FLAG_RESTART, + "check_blocks"); + return; + } + +#endif extent_fs = (ctx->fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS); @@ -1967,8 +2041,15 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, } } +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + if ((!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && + /* snapshot file always supports the 'huge_file' flag */ + !(inode->i_flags & EXT4_SNAPFILE_FL)) || +#else if (!(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || +#endif !(inode->i_flags & EXT4_HUGE_FILE_FL)) pb.num_blocks *= (fs->blocksize / 512); #if 0 @@ -1999,6 +2080,9 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, !(inode->i_flags & EXT4_EOFBLOCKS_FL)) bad_size = 3; else if (!(extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) && +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + !(pb.is_reg && (inode->i_flags & EXT4_SNAPFILE_FL)) && +#endif size > ext2_max_sizes[fs->super->s_log_block_size]) /* too big for a direct/indirect-mapped file */ bad_size = 4; @@ -2037,8 +2121,15 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, (inode->i_size_high || inode->i_size & 0x80000000UL)) ctx->large_files++; if ((pb.num_blocks != ext2fs_inode_i_blocks(fs, inode)) || +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + (((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + /* snapshot file always supports the 'huge_file' flag */ + (inode->i_flags & EXT4_SNAPFILE_FL)) && +#else ((fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && +#endif (inode->i_flags & EXT4_HUGE_FILE_FL) && (inode->osd2.linux2.l_i_blocks_hi != 0))) { pctx->num = pb.num_blocks; @@ -2236,6 +2327,11 @@ static int process_block(ext2_filsys fs, mark_block_used(ctx, blk); } else mark_block_used(ctx, blk); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + /* mark snapshot file blocks excluded */ + if (p->snapfile && ctx->block_excluded_map) + ext2fs_fast_mark_block_bitmap2(ctx->block_excluded_map, blk); +#endif p->num_blocks++; if (blockcnt >= 0) p->last_block = blockcnt; diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c index bc3bf02..b744f6a 100644 --- a/e2fsck/pass5.c +++ b/e2fsck/pass5.c @@ -14,6 +14,9 @@ #include "problem.h" static void check_block_bitmaps(e2fsck_t ctx); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +static void check_exclude_bitmaps(e2fsck_t ctx); +#endif static void check_inode_bitmaps(e2fsck_t ctx); static void check_inode_end(e2fsck_t ctx); static void check_block_end(e2fsck_t ctx); @@ -44,6 +47,11 @@ void e2fsck_pass5(e2fsck_t ctx) check_block_bitmaps(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + check_exclude_bitmaps(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; +#endif check_inode_bitmaps(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; @@ -60,6 +68,11 @@ void e2fsck_pass5(e2fsck_t ctx) ctx->inode_dir_map = 0; ext2fs_free_block_bitmap(ctx->block_found_map); ctx->block_found_map = 0; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (ctx->block_excluded_map) + ext2fs_free_block_bitmap(ctx->block_excluded_map); + ctx->block_excluded_map = 0; +#endif print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io); } @@ -82,6 +95,20 @@ static void print_bitmap_problem(e2fsck_t ctx, int problem, else problem = PR_5_BLOCK_RANGE_USED; break; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + case PR_5_BLOCK_NOTEXCLUDED: + if (pctx->blk == pctx->blk2) + pctx->blk2 = 0; + else + problem = PR_5_BLOCK_RANGE_NOTEXCLUDED; + break; + case PR_5_BLOCK_EXCLUDED: + if (pctx->blk == pctx->blk2) + pctx->blk2 = 0; + else + problem = PR_5_BLOCK_RANGE_EXCLUDED; + break; +#endif case PR_5_INODE_UNUSED: if (pctx->ino == pctx->ino2) pctx->ino2 = 0; @@ -294,6 +321,11 @@ redo_counts: ext2fs_unmark_valid(fs); for (i = 0; i < fs->group_desc_count; i++) { +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + if (fs->super->s_flags & EXT2_FLAGS_IS_SNAPSHOT) + /* ignore group block counts in next3 snapshot image */ + break; +#endif if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) { pctx.group = i; pctx.blk = fs->group_desc[i].bg_free_blocks_count; @@ -323,6 +355,142 @@ errout: ext2fs_free_mem(&free_array); } +#define blk64_t blk_t +#define ext2fs_bg_flags_test(fs, group, flag) \ + (fs->group_desc[group].bg_flags & flag) +#define ext2fs_bg_flags_clear(fs, group, flag) \ + fs->group_desc[group].bg_flags &= ~flag +#define ext2fs_blocks_count(sb) \ + (sb)->s_blocks_count +#define ext2fs_fast_test_block_bitmap2 ext2fs_fast_test_block_bitmap + +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +static void check_exclude_bitmaps(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + blk64_t i; + int group = 0; + int blocks = 0; + int actual, bitmap; + struct problem_context pctx; + int problem, save_problem, fixit, had_problem; + errcode_t retval; + int csum_flag; + int skip_group = 0; + + clear_problem_context(&pctx); + + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP)) + return; + + csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM); +redo_counts: + had_problem = 0; + save_problem = 0; + pctx.blk = pctx.blk2 = NO_BLK; + if (csum_flag && + (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) + skip_group++; + for (i = fs->super->s_first_data_block; + i < ext2fs_blocks_count(fs->super); + i++) { + actual = ext2fs_fast_test_block_bitmap2(ctx->block_excluded_map, i); + + if (skip_group) { + bitmap = 0; + actual = (actual != 0); + } else + bitmap = ext2fs_fast_test_block_bitmap2(fs->exclude_map, i); + + if (actual == bitmap) + goto do_counts; + + if (!actual && bitmap) { + /* + * Block not excluded, but marked in exclude bitmap. + */ + problem = PR_5_BLOCK_NOTEXCLUDED; + } else { + /* + * Block excluded, but not marked in exclude bitmap. + */ + problem = PR_5_BLOCK_EXCLUDED; + + if (skip_group) { + struct problem_context pctx2; + pctx2.blk = i; + pctx2.group = group; + if (fix_problem(ctx, PR_5_BLOCK_UNINIT,&pctx2)){ + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + skip_group = 0; + } + } + } + if (pctx.blk == NO_BLK) { + pctx.blk = pctx.blk2 = i; + save_problem = problem; + } else { + if ((problem == save_problem) && + (pctx.blk2 == i-1)) + pctx.blk2++; + else { + print_bitmap_problem(ctx, save_problem, &pctx); + pctx.blk = pctx.blk2 = i; + save_problem = problem; + } + } + ctx->flags |= E2F_FLAG_PROG_SUPPRESS; + had_problem++; + + do_counts: + blocks ++; + if ((blocks == fs->super->s_blocks_per_group) || + (i == fs->super->s_blocks_count-1)) { + group ++; + blocks = 0; + skip_group = 0; + if (ctx->progress) + if ((ctx->progress)(ctx, 5, group, + fs->group_desc_count*2)) + return; + if (csum_flag && + (i != ext2fs_blocks_count(fs->super)-1) && + ext2fs_bg_flags_test(fs, group, + EXT2_BG_BLOCK_UNINIT)) + skip_group++; + } + } + if (pctx.blk != NO_BLK) + print_bitmap_problem(ctx, save_problem, &pctx); + if (had_problem) + fixit = end_problem_latch(ctx, PR_LATCH_XBITMAP); + else + fixit = -1; + ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; + + if (fixit == 1) { + ext2fs_free_block_bitmap(fs->exclude_map); + retval = ext2fs_copy_bitmap(ctx->block_excluded_map, + &fs->exclude_map); + if (retval) { + clear_problem_context(&pctx); + fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ext2fs_mark_exclude_dirty(fs); + /* clear fix_exclude flag */ + if (fs->super->s_flags & EXT2_FLAGS_FIX_EXCLUDE) { + fs->super->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE; + ext2fs_mark_super_dirty(fs); + } + } else if (fixit == 0) + ext2fs_unmark_valid(fs); +} + +#endif static void check_inode_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 8f0b211..e282875 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -40,7 +40,15 @@ #define PROMPT_UNLINK 17 #define PROMPT_CLEAR_HTREE 18 #define PROMPT_RECREATE 19 +#ifdef EXT2FS_SNAPSHOT_CHECK_LIST +#define PROMPT_TERMINATE_LIST 20 +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT +#define PROMPT_CLEAR_SNAPSHOTS 21 +#endif +#define PROMPT_NULL 22 +#else #define PROMPT_NULL 20 +#endif /* * These are the prompts which are used to ask the user if they want @@ -67,7 +75,15 @@ static const char *prompt[] = { N_("Unlink"), /* 17 */ N_("Clear HTree index"),/* 18 */ N_("Recreate"), /* 19 */ +#ifdef EXT2FS_SNAPSHOT_CHECK_LIST + N_("Terminate list"), /* 20 */ +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + N_("Clear all snapshots"),/* 21 */ +#endif + "", /* 22 */ +#else "", /* 20 */ +#endif }; /* @@ -95,7 +111,15 @@ static const char *preen_msg[] = { N_("UNLINKED"), /* 17 */ N_("HTREE INDEX CLEARED"),/* 18 */ N_("WILL RECREATE"), /* 19 */ +#ifdef EXT2FS_SNAPSHOT_CHECK_LIST + N_("LIST TERMINATED"), /* 20 */ +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + N_("SNAPSHOTS CLEARED"),/* 21 */ +#endif + "", /* 22 */ +#else "", /* 20 */ +#endif }; static struct e2fsck_problem problem_table[] = { @@ -332,6 +356,38 @@ static struct e2fsck_problem problem_table[] = { N_("Resize @i not valid. "), PROMPT_RECREATE, 0 }, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + /* Exclude not enabled, but exclude inode is non-zero */ + { PR_0_CLEAR_EXCLUDE_INODE, + N_("Exclude_@i not enabled, but the exclude @i is non-zero. "), + PROMPT_CLEAR, 0 }, + + /* Exclude inode invalid */ + { PR_0_EXCLUDE_INODE_INVALID, + N_("Exclude @i not valid. "), + PROMPT_RECREATE, 0 }, + +#endif +#ifdef EXT2FS_SNAPSHOT_CHECK_LIST + /* Bad snapshot on list */ + { PR_0_BAD_SNAPSHOT_LIST, + N_("Bad @i found on snapshot list. "), + PROMPT_TERMINATE_LIST, PR_PREEN_OK }, + +#endif +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + /* Corrupted snapshot */ + { PR_0_BAD_SNAPSHOT, + N_("@f may contain corrupted snapshots.\n" + "This version of e2fsck does not support fixing snapshots.\n"), + PROMPT_CLEAR_SNAPSHOTS, 0 }, + + /* Clear all snapshots */ + { PR_0_CLEAR_SNAPSHOTS, + N_("Snapshots may be damaged by repair. "), + PROMPT_CLEAR_SNAPSHOTS, PR_PREEN_OK }, + +#endif /* Last mount time is in the future */ { PR_0_FUTURE_SB_LAST_MOUNT, N_("@S last mount time (%t,\n\tnow = %T) is in the future.\n"), @@ -800,6 +856,13 @@ static struct e2fsck_problem problem_table[] = { N_("Resize @i (re)creation failed: %m."), PROMPT_CONTINUE, 0 }, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + /* Exclude inode failed */ + { PR_1_EXCLUDE_INODE_CREATE, + N_("Exclude @i (re)creation failed: %m."), + PROMPT_CLEAR, 0 }, + +#endif /* invalid inode->i_extra_isize */ { PR_1_EXTRA_ISIZE, N_("@i %i has a extra size (%IS) which is @n\n"), @@ -1533,6 +1596,28 @@ static struct e2fsck_problem problem_table[] = { "\n", PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + /* Exclude bitmap differences header */ + { PR_5_EXCLUDE_BITMAP_HEADER, + N_("Exclude @B differences: "), + PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG}, + + /* Block not excluded, but marked in exclude bitmap */ + { PR_5_BLOCK_NOTEXCLUDED, + " -%b", + PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Block excluded, but not marked in exclude bitmap */ + { PR_5_BLOCK_EXCLUDED, + " +%b", + PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Exclude bitmap differences end */ + { PR_5_EXCLUDE_BITMAP_END, + "\n", + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + +#endif /* Inode bitmap differences header */ { PR_5_INODE_BITMAP_HEADER, N_("@i @B differences: "), @@ -1609,6 +1694,18 @@ static struct e2fsck_problem problem_table[] = { " +(%b--%c)", PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + /* Block range not excluded, but marked in exclude bitmap */ + { PR_5_BLOCK_RANGE_NOTEXCLUDED, + " -(%b--%c)", + PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Block range excluded, but not marked in exclude bitmap */ + { PR_5_BLOCK_RANGE_EXCLUDED, + " +(%b--%c)", + PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + +#endif /* Inode range not used, but marked in bitmap */ { PR_5_INODE_RANGE_UNUSED, " -(%i--%j)", @@ -1650,6 +1747,9 @@ static struct latch_descr pr_latch_info[] = { { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 }, { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END }, { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END }, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + { PR_LATCH_XBITMAP, PR_5_EXCLUDE_BITMAP_HEADER, PR_5_EXCLUDE_BITMAP_END }, +#endif { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 }, { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END }, { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 }, diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 7c4c156..d0dc047 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -39,6 +39,9 @@ struct problem_context { #define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */ #define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */ #define PR_LATCH_BG_CHECKSUM 0x00A0 /* Latch for block group checksums */ +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +#define PR_LATCH_XBITMAP 0x00B0 /* Latch for pass 5 exclude bitmap proc. */ +#endif #define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1) @@ -227,6 +230,27 @@ struct problem_context { /* Block group checksum (latch question) */ #define PR_0_GDT_CSUM_LATCH 0x00003E +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +/* Exclude_inode not enabled, but exclude inode is non-zero */ +#define PR_0_CLEAR_EXCLUDE_INODE 0x000100 + +/* Exclude inode invalid */ +#define PR_0_EXCLUDE_INODE_INVALID 0x000101 + +#endif +#ifdef EXT2FS_SNAPSHOT_CHECK_LIST +/* Bas snapshot on list */ +#define PR_0_BAD_SNAPSHOT_LIST 0x000102 + +#endif +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT +/* Corrupted snapshot */ +#define PR_0_BAD_SNAPSHOT 0x000103 + +/* Clear all snapshots */ +#define PR_0_CLEAR_SNAPSHOTS 0x000104 + +#endif /* * Pass 1 errors @@ -520,6 +544,11 @@ struct problem_context { /* EOFBLOCKS flag set when not necessary */ #define PR_1_EOFBLOCKS_FL_SET 0x010060 +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +/* Exclude inode failed */ +#define PR_1_EXCLUDE_INODE_CREATE 0x010100 + +#endif /* * Pass 1b errors */ @@ -983,6 +1012,26 @@ struct problem_context { /* Inode in use but group is marked INODE_UNINIT */ #define PR_5_INODE_UNINIT 0x050019 +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +/* Exclude bitmap differences header */ +#define PR_5_EXCLUDE_BITMAP_HEADER 0x050100 + +/* Block not excluded, but marked in exclude bitmap */ +#define PR_5_BLOCK_NOTEXCLUDED 0x050101 + +/* Block excluded, but not marked in exclude bitmap */ +#define PR_5_BLOCK_EXCLUDED 0x050102 + +/* Block range not excluded, but marked in exclude bitmap */ +#define PR_5_BLOCK_RANGE_NOTEXCLUDED 0x050103 + +/* Block range excluded, but not marked in exclude bitmap */ +#define PR_5_BLOCK_RANGE_EXCLUDED 0x050104 + +/* Exclude bitmap differences end */ +#define PR_5_EXCLUDE_BITMAP_END 0x050105 + +#endif /* * Post-Pass 5 errors */ diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index ceb8543..6db3f90 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -829,6 +829,15 @@ void e2fsck_rehash_directories(e2fsck_t ctx) int cur, max, all_dirs, dir_index, first = 1; init_resource_track(&rtrack, ctx->fs->io); +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + + /* never rehash directories when scanning volume with active snapshot */ + if ((ctx->fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) && + ctx->fs->super->s_snapshot_inum) + return; + +#endif all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS; if (!ctx->dirs_to_hash && !all_dirs) diff --git a/e2fsck/super.c b/e2fsck/super.c index accc2f1..8762d67 100644 --- a/e2fsck/super.c +++ b/e2fsck/super.c @@ -229,6 +229,14 @@ static int release_orphan_inodes(e2fsck_t ctx) struct problem_context pctx; char *block_buf; +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + /* never release orphans when scanning volume with active snapshot */ + if ((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) && + fs->super->s_snapshot_inum) + return 0; + +#endif if ((ino = fs->super->s_last_orphan) == 0) return 0; @@ -423,6 +431,239 @@ cleanup: } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +/* + * Check the exclude inode to make sure it is sane. We check both for + * the case where exclude bitmap is not enabled (in which case the + * exclude inode should be cleared) as well as the case where exclude + * bitmap is enabled. + */ +void check_exclude_inode(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + struct ext2_inode inode; + struct problem_context pctx; + int i, flags = 0; + blk_t blk; + errcode_t retval; + + clear_problem_context(&pctx); + + /* Read the exclude inode */ + pctx.ino = EXT2_EXCLUDE_INO; + retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO, &inode); + if (retval) { + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) + ctx->flags |= E2F_FLAG_EXCLUDE_INODE; + return; + } + + /* + * If the exclude inode feature isn't set, check to make sure + * the exclude inode is cleared; then we're done. + */ + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) { + for (i=0; i < EXT2_N_BLOCKS; i++) { + if (inode.i_block[i]) + break; + } + if ((i < EXT2_N_BLOCKS) && + fix_problem(ctx, PR_0_CLEAR_EXCLUDE_INODE, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode, + "clear_exclude"); + } + return; + } + + /* + * The exclude inode feature is enabled; check to make sure the + * only block in use is the double indirect block + */ + blk = inode.i_block[EXT2_DIND_BLOCK]; + for (i=0; i < EXT2_N_BLOCKS; i++) { + if (i != EXT2_DIND_BLOCK && inode.i_block[i]) + break; + } + if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count || + !(inode.i_mode & LINUX_S_IFREG) || + (blk < fs->super->s_first_data_block || + blk >= fs->super->s_blocks_count)) { + if (fix_problem(ctx, PR_0_EXCLUDE_INODE_INVALID, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode, + "clear_exclude"); + ctx->flags |= E2F_FLAG_EXCLUDE_INODE; + } + return; + } + + if (!(ctx->options & E2F_OPT_READONLY)) + flags = EXCLUDE_ALLOC; + /* + * create exclude inode and/or allocate missing exclude bitmap blocks. + */ + clear_problem_context(&pctx); + pctx.errcode = ext2fs_create_exclude_inode(fs, flags); + if (pctx.errcode && + fix_problem(ctx, PR_1_EXCLUDE_INODE_CREATE, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode, + "clear_exclude"); + ctx->flags |= E2F_FLAG_EXCLUDE_INODE; + } +} + +#endif +#ifdef EXT2FS_SNAPSHOT_CHECK_LIST +/* + * Check that snapshot list contains valid snapshot files. + * Returns the number of valid snapshots on list. + * + * TODO: cleanup orphan snapshot files (not found on list) + */ +static int check_snapshot_list(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + struct ext2_super_block *sb = fs->super; + struct ext2_inode inode; + struct ext2_inode inode_prev; + ext2_ino_t ino = sb->s_snapshot_list; + ext2_ino_t ino_prev = 0; + errcode_t retval = 0; + struct problem_context pctx; + int i = 0; + + if (!ino && sb->s_snapshot_inum) { + /* reset snapshot list head to active snapshot */ + ino = sb->s_snapshot_list = sb->s_snapshot_inum; + ext2fs_mark_super_dirty(fs); + } + if (ino) + fputs(_("Checking snapshots: "), stderr); + + while (ino) { + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval || !(inode.i_flags & EXT4_SNAPFILE_FL) || + !LINUX_S_ISREG(inode.i_mode) || + inode.i_dtime) { + if (ino == sb->s_snapshot_list) { + /* reset list head */ + sb->s_snapshot_list = 0; + ext2fs_mark_super_dirty(fs); + ino = sb->s_snapshot_inum; + continue; + } + clear_problem_context(&pctx); + if (!fix_problem(ctx, PR_0_BAD_SNAPSHOT_LIST, &pctx)) + break; + + /* disconnect inode from snapshot list */ + if (ino == sb->s_snapshot_inum) { + /* reset active snapshot */ + sb->s_snapshot_inum = 0; + ext2fs_mark_super_dirty(fs); + } + if (ino_prev) { + /* terminate list at prev inode */ + inode_prev.i_next_snapshot = 0; + e2fsck_write_inode(ctx, ino_prev, &inode_prev, + "terminate snapshot list"); + } + break; + } + + fprintf(stderr, _("%u,"), inode.i_generation); + inode_prev = inode; + ino_prev = ino; + ino = inode.i_next_snapshot; + i++; + } + + if (ino_prev && !ino) + fputs(_("done\n"), stderr); + return i; +} + +#endif +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT +/* + * This function checks if the file system has snapshots + */ +void check_snapshots(e2fsck_t ctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + struct problem_context pctx; + int cont, clear_snapshots = 0; + + if (!(sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) + /* no snapshots */ + return; + +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + clear_problem_context(&pctx); + if (ctx->options & E2F_OPT_CLEAR_SNAPSHOTS) { + if (fix_problem(ctx, PR_0_CLEAR_SNAPSHOTS, &pctx)) + clear_snapshots = 1; + /* don't ask again after pass1 restart */ + ctx->options &= ~E2F_OPT_CLEAR_SNAPSHOTS; + } + else if (sb->s_flags & EXT2_FLAGS_FIX_SNAPSHOT) { + /* corrupted snapshot need to be fixed */ + if (fix_problem(ctx, PR_0_BAD_SNAPSHOT, &pctx)) + clear_snapshots = 1; + } + if (clear_snapshots) { + if (sb->s_snapshot_list || sb->s_snapshot_inum || + (sb->s_flags & (EXT2_FLAGS_FIX_SNAPSHOT | + EXT2_FLAGS_IS_SNAPSHOT))) { + /* reset snapshot list head */ + sb->s_snapshot_list = sb->s_snapshot_inum = 0; + sb->s_flags &= ~(EXT2_FLAGS_FIX_SNAPSHOT | + EXT2_FLAGS_IS_SNAPSHOT); + ext2fs_mark_super_dirty(ctx->fs); + } + /* clear all snapshot inodes (in pass1) */ + ctx->flags |= E2F_FLAG_CLEAR_SNAPSHOTS; + if (sb->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) + /* and reset exclude bitmap */ + ctx->flags |= E2F_FLAG_EXCLUDE_INODE; + return; + } + +#endif +#ifdef EXT2FS_SNAPSHOT_CHECK_LIST + if (!check_snapshot_list(ctx)) + /* no valid snapshots on list */ + return; + +#endif + if (!sb->s_snapshot_inum) + /* no active snapshot */ + return; + + if ((ctx->options & E2F_OPT_PREEN) || + (ctx->options & E2F_OPT_NO)) + /* preen and readonly modes are snapshot friendly */ + return; + + printf(_("%s has snapshots. "), ctx->filesystem_name); + if (!ctx->interactive) + fatal_error(ctx, _("Cannot continue, aborting.\n\n")); + printf(_("\n\n\007\007\007\007WARNING!!! " + "Running e2fsck on filesystem with snapshots may\n" + "damage the snapshots.\007\007\007\n\n")); + cont = ask_yn(_("Do you really want to continue"), -1); + if (!cont) { + printf (_("check aborted.\n")); + exit (0); + } +} + +#endif /* * This function checks the dirhash signed/unsigned hint if necessary. */ @@ -450,6 +691,72 @@ static void e2fsck_fix_dirhash_hint(e2fsck_t ctx) } } +#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER +/* + * This function prints the message buffer at the end of super block. + */ +static void e2fsck_print_message_buffer(e2fsck_t ctx) +{ + char *buf; + int n, len = ctx->fs->blocksize - MSGBUF_OFFSET; + unsigned offset = 0; + int retval; + + if (len < MSGBUF_OFFSET) + /* 1K or 2K fs->blocksize */ + return; + + buf = (char *) e2fsck_allocate_memory(ctx, len, "message buffer"); + + /* keep it simple - write in MSGBUF_OFFSET blocksize */ + io_channel_set_blksize(ctx->fs->io, MSGBUF_OFFSET); + n = len / MSGBUF_OFFSET; + /* read message buffer from super block + 2K */ + retval = io_channel_read_blk(ctx->fs->io, 1, n, buf); + if (retval || !*buf) + goto out; + + /* print messages in buffer */ + puts("Error messages recorded in message buffer:"); + while (offset < len && buf[offset]) { + puts(buf+offset); + offset += MSGBUF_RECLEN; + } + puts("End of message buffer."); +out: + io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize); + ext2fs_free_mem(&buf); +} + +/* + * This function clears the message buffer at the end of super block. + * It is called before clearing the EXT2_ERROR_FS flag from super block. + */ +void e2fsck_clear_message_buffer(e2fsck_t ctx) +{ + char *buf; + int n, len = ctx->fs->blocksize - MSGBUF_OFFSET; + unsigned offset = 0; + int retval; + + if (len < MSGBUF_OFFSET) + /* 1K or 2K fs->blocksize */ + return; + + buf = (char *) e2fsck_allocate_memory(ctx, len, "message buffer"); + + /* keep it simple - write in MSGBUF_OFFSET blocksize */ + io_channel_set_blksize(ctx->fs->io, MSGBUF_OFFSET); + n = len / MSGBUF_OFFSET; + + /* clear message buffer at super block + 2K */ + memset(buf, 0, len); + io_channel_write_blk(ctx->fs->io, 1, n, buf); + io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize); + ext2fs_free_mem(&buf); +} + +#endif void check_super_block(e2fsck_t ctx) { @@ -741,7 +1048,7 @@ void check_super_block(e2fsck_t ctx) * Check to see if we should disable the test_fs flag */ profile_get_boolean(ctx->profile, "options", - "clear_test_fs_flag", 0, 1, + "clear_test_fs_flag", 0, 0, &clear_test_fs_flag); if (!(ctx->options & E2F_OPT_READONLY) && clear_test_fs_flag && @@ -869,6 +1176,13 @@ void check_super_block(e2fsck_t ctx) */ e2fsck_fix_dirhash_hint(ctx); +#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER + /* + * Print message buffer if necessary + */ + e2fsck_print_message_buffer(ctx); + +#endif return; } diff --git a/e2fsck/unix.c b/e2fsck/unix.c index 624e11b..4ea94c9 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -73,7 +73,11 @@ int journal_enable_debug = -1; static void usage(e2fsck_t ctx) { fprintf(stderr, +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + _("Usage: %s [-panyrcdfvtxDFV] [-b superblock] [-B blocksize]\n" +#else _("Usage: %s [-panyrcdfvtDFV] [-b superblock] [-B blocksize]\n" +#endif "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n" "\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\n" "\t\t[-E extended-options] device\n"), @@ -92,6 +96,9 @@ static void usage(e2fsck_t ctx) " -j external_journal Set location of the external journal\n" " -l bad_blocks_file Add to badblocks list\n" " -L bad_blocks_file Set badblocks list\n" +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + " -x Delete all snapshots\n" +#endif )); exit(FSCK_USAGE); @@ -676,7 +683,11 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) ctx->program_name = *argv; else ctx->program_name = "e2fsck"; +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkx")) != EOF) +#else while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF) +#endif switch (c) { case 'C': ctx->progress = e2fsck_update_progress; @@ -806,6 +817,11 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) case 'k': keep_bad_blocks++; break; +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + case 'x': + ctx->options |= E2F_OPT_CLEAR_SNAPSHOTS; + break; +#endif default: usage(ctx); } @@ -819,6 +835,14 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) _("The -n and -D options are incompatible.")); fatal_error(ctx, 0); } +#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT + if ((ctx->options & E2F_OPT_NO) && + (ctx->options & E2F_OPT_CLEAR_SNAPSHOTS)) { + com_err(ctx->program_name, 0, + _("The -n and -x options are incompatible.")); + fatal_error(ctx, 0); + } +#endif if ((ctx->options & E2F_OPT_NO) && cflag) { com_err(ctx->program_name, 0, _("The -n and -c options are incompatible.")); @@ -1332,6 +1356,12 @@ print_unsupp_features: fatal_error(ctx, 0); check_if_skip(ctx); check_resize_inode(ctx); +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + check_snapshots(ctx); +#endif +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + check_exclude_inode(ctx); +#endif if (bad_blocks_file) read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks); else if (cflag) @@ -1465,6 +1495,9 @@ no_journal: sb->s_lastcheck = ctx->now; memset(((char *) sb) + EXT4_S_ERR_START, 0, EXT4_S_ERR_LEN); +#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER + e2fsck_clear_message_buffer(ctx); +#endif ext2fs_mark_super_dirty(fs); } } diff --git a/install_ext4dev.sh b/install_ext4dev.sh new file mode 100755 index 0000000..61595f8 --- /dev/null +++ b/install_ext4dev.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# Install ext4dev patched e2fsprogs + +# build e2fsprogs from e2fsprogs-snapshots git +#./configure +make +# install progs with .ext4dev suffix +install -T e2fsck/e2fsck /sbin/fsck.ext4dev +install -T misc/mke2fs /sbin/mkfs.ext4dev +install -T misc/tune2fs /sbin/tunefs.ext4dev +install -T misc/dumpe2fs /sbin/dumpfs.ext4dev +install -T misc/lsattr /sbin/lsattr.ext4dev +install -T misc/chattr /sbin/chattr.ext4dev +install -T misc/lsattr /sbin/lssnap +install -T misc/chattr /sbin/chsnap +install -T resize/resize2fs /sbin/resize.ext4dev +install -T contrib/e4snapshot /sbin/snapshot.ext4dev diff --git a/install_next3.sh b/install_next3.sh new file mode 100755 index 0000000..648aac0 --- /dev/null +++ b/install_next3.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Install next3 patched e2fsprogs + +# build e2fsprogs from e2fsprogs-snapshots git +#./configure +make +# install progs with .next3 suffix +install -T e2fsck/e2fsck /sbin/fsck.next3 +install -T misc/mke2fs /sbin/mkfs.next3 +install -T misc/tune2fs /sbin/tunefs.next3 +install -T misc/dumpe2fs /sbin/dumpfs.next3 +install -T misc/lsattr /sbin/lsattr.next3 +install -T misc/chattr /sbin/chattr.next3 +install -T resize/resize2fs /sbin/resize.next3 +install -T contrib/e4snapshot /sbin/snapshot.next3 + diff --git a/install_next4.sh b/install_next4.sh new file mode 100755 index 0000000..d7ee017 --- /dev/null +++ b/install_next4.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# Install next4 patched e2fsprogs + +# build e2fsprogs from e2fsprogs-snapshots git +#./configure +make +# install progs with .next4 suffix +install -T e2fsck/e2fsck /sbin/fsck.next4 +install -T misc/mke2fs /sbin/mkfs.next4 +install -T misc/tune2fs /sbin/tunefs.next4 +install -T misc/dumpe2fs /sbin/dumpfs.next4 +install -T misc/lsattr /sbin/lsattr.next4 +install -T misc/chattr /sbin/chattr.next4 +install -T misc/lsattr /sbin/lssnap +install -T misc/chattr /sbin/chsnap +install -T resize/resize2fs /sbin/resize.next4 +install -T contrib/e4snapshot /sbin/snapshot.next4 diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h index 7a90c37..f3c9ab0 100644 --- a/lib/e2p/e2p.h +++ b/lib/e2p/e2p.h @@ -26,8 +26,18 @@ /* `options' for print_flags() */ #define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */ +#ifdef EXT2FS_SNAPSHOT_CTL +#define PFOPT_SNAPSHOT 2 /* list/control snapshot flags */ +#define PFOPT_SNAPSHOT_X 4 /* for backward compatibility with next3 */ +#endif + + +#ifdef EXT2FS_SNAPSHOT_CTL +int fgetsnapflags(const char * name, unsigned long * flags); +int fsetsnapflags(const char * name, unsigned long flags); +#endif int fgetflags (const char * name, unsigned long * flags); int fgetversion (const char * name, unsigned long * version); int fsetflags (const char * name, unsigned long flags); diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c index c7f8a35..d1ef001 100644 --- a/lib/e2p/feature.c +++ b/lib/e2p/feature.c @@ -40,6 +40,14 @@ static struct feature feature_list[] = { "resize_inode" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG, "lazy_bg" }, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE, + "exclude_inode" }, +#endif +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP, + "exclude_bitmap" }, +#endif { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, "sparse_super" }, @@ -55,6 +63,12 @@ static struct feature feature_list[] = { "dir_nlink" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE, "extra_isize" }, +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT, + "snapshots" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT, + "has_snapshot" }, +#endif { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION, "compression" }, diff --git a/lib/e2p/fgetflags.c b/lib/e2p/fgetflags.c index d66f8e1..33037cf 100644 --- a/lib/e2p/fgetflags.c +++ b/lib/e2p/fgetflags.c @@ -40,7 +40,7 @@ #define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) #endif -int fgetflags (const char * name, unsigned long * flags) +static int fgetflags_ioctl(const char * name, unsigned long * flags, int ioc) { struct stat buf; #if HAVE_STAT_FLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS) @@ -75,7 +75,7 @@ int fgetflags (const char * name, unsigned long * flags) fd = open (name, OPEN_FLAGS); if (fd == -1) return -1; - r = ioctl (fd, EXT2_IOC_GETFLAGS, &f); + r = ioctl (fd, ioc, &f); if (r == -1) save_errno = errno; *flags = f; @@ -84,10 +84,10 @@ int fgetflags (const char * name, unsigned long * flags) errno = save_errno; return r; #else - f = -1; - save_errno = syscall(SYS_fsctl, name, EXT2_IOC_GETFLAGS, &f, 0); - *flags = f; - return (save_errno); + f = -1; + save_errno = syscall(SYS_fsctl, name, ioc, &f, 0); + *flags = f; + return (save_errno); #endif #endif /* HAVE_EXT2_IOCTLS */ #endif @@ -95,3 +95,16 @@ notsupp: errno = EOPNOTSUPP; return -1; } + +int fgetflags(const char * name, unsigned long * flags) +{ + return fgetflags_ioctl(name, flags, EXT2_IOC_GETFLAGS); +} + +#ifdef EXT2FS_SNAPSHOT_CTL +int fgetsnapflags(const char * name, unsigned long * flags) +{ + return fgetflags_ioctl(name, flags, EXT2_IOC_GETSNAPFLAGS); +} + +#endif diff --git a/lib/e2p/fsetflags.c b/lib/e2p/fsetflags.c index 30437a2..e67f2e1 100644 --- a/lib/e2p/fsetflags.c +++ b/lib/e2p/fsetflags.c @@ -49,7 +49,7 @@ #define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) #endif -int fsetflags (const char * name, unsigned long flags) +int fsetflags_ioctl(const char * name, unsigned long flags, int ioc) { struct stat buf; #if HAVE_CHFLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS) @@ -82,15 +82,15 @@ int fsetflags (const char * name, unsigned long flags) if (fd == -1) return -1; f = (int) flags; - r = ioctl (fd, EXT2_IOC_SETFLAGS, &f); + r = ioctl (fd, ioc, &f); if (r == -1) save_errno = errno; close (fd); if (save_errno) errno = save_errno; #else - f = (int) flags; - return syscall(SYS_fsctl, name, EXT2_IOC_SETFLAGS, &f, 0); + f = (int) flags; + return syscall(SYS_fsctl, name, ioc, &f, 0); #endif return r; #endif /* HAVE_EXT2_IOCTLS */ @@ -99,3 +99,16 @@ notsupp: errno = EOPNOTSUPP; return -1; } + +int fsetflags(const char * name, unsigned long flags) +{ + return fsetflags_ioctl(name, flags, EXT2_IOC_SETFLAGS); +} + +#ifdef EXT2FS_SNAPSHOT_CTL +int fsetsnapflags(const char * name, unsigned long flags) +{ + return fsetflags_ioctl(name, flags, EXT2_IOC_SETSNAPFLAGS); +} + +#endif diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c index 5ef8098..3ebc07f 100644 --- a/lib/e2p/ls.c +++ b/lib/e2p/ls.c @@ -160,6 +160,20 @@ static void print_super_flags(struct ext2_super_block * s, FILE *f) fputs("test_filesystem ", f); flags_found++; } +#ifdef EXT2FS_SNAPSHOT_ON_DISK + if (s->s_flags & EXT2_FLAGS_IS_SNAPSHOT) { + fputs("is_snapshot ", f); + flags_found++; + } + if (s->s_flags & EXT2_FLAGS_FIX_SNAPSHOT) { + fputs("fix_snapshot ", f); + flags_found++; + } + if (s->s_flags & EXT2_FLAGS_FIX_EXCLUDE) { + fputs("fix_exclude ", f); + flags_found++; + } +#endif if (flags_found) fputs("\n", f); else @@ -315,6 +329,17 @@ void list_super2(struct ext2_super_block * sb, FILE *f) if (sb->s_last_orphan) fprintf(f, "First orphan inode: %u\n", sb->s_last_orphan); + if (sb->s_snapshot_inum) { + fprintf(f, "Snapshot inode: %u\n", + sb->s_snapshot_inum); + fprintf(f, "Snapshot ID: %u\n", + sb->s_snapshot_id); + fprintf(f, "Snapshot reserved blocks: %llu\n", + sb->s_snapshot_r_blocks_count); + } + if (sb->s_snapshot_list) + fprintf(f, "Snapshot list first inode: %u\n", + sb->s_snapshot_list); if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || sb->s_def_hash_version) fprintf(f, "Default directory hash: %s\n", diff --git a/lib/e2p/pf.c b/lib/e2p/pf.c index f34a5cc..747edde 100644 --- a/lib/e2p/pf.c +++ b/lib/e2p/pf.c @@ -48,16 +48,59 @@ static struct flags_name flags_array[] = { { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" }, { EXT4_EXTENTS_FL, "e", "Extents" }, { EXT4_HUGE_FILE_FL, "h", "Huge_file" }, +#ifdef EXT2FS_SNAPSHOT_CTL + { EXT4_SNAPFILE_FL, "x", "Snapshot_File" }, +#endif + { 0, NULL, NULL } +}; + +#ifdef EXT2FS_SNAPSHOT_CTL +/* Snapshot dynamic state flags */ +static struct flags_name snapshot_flags_array[] = { + { 1UL<<EXT4_SNAPSHOT_LIST, "S", "on_liSt" }, + { 1UL<<EXT4_SNAPSHOT_ENABLED, "n", "eNabled" }, + { 1UL<<EXT4_SNAPSHOT_ACTIVE, "a", "Active" }, + { 1UL<<EXT4_SNAPSHOT_INUSE, "p", "inuse_by_Previous" }, + { 1UL<<EXT4_SNAPSHOT_DELETED, "s", "Deleted" }, + { 1UL<<EXT4_SNAPSHOT_SHRUNK, "h", "sHrunk" }, + { 1UL<<EXT4_SNAPSHOT_OPEN, "o", "mOunted" }, + { 1UL<<EXT4_SNAPSHOT_TAGGED, "t", "Tagged" }, { 0, NULL, NULL } }; +#ifdef EXT2FS_SNAPSHOT_CTL_OLD +/* Old snapshot flags for backward compatibility with next3 */ +static struct flags_name snapshot_X_flags_array[] = { + { NEXT3_SNAPFILE_LIST_FL, "S", "on_liSt" }, + { NEXT3_SNAPFILE_ENABLED_FL, "n", "eNabled" }, + { NEXT3_SNAPFILE_ACTIVE_FL, "a", "Active" }, + { NEXT3_SNAPFILE_INUSE_FL, "p", "inuse_by_Previous" }, + { NEXT3_SNAPFILE_DELETED_FL, "s", "Deleted" }, + { NEXT3_SNAPFILE_SHRUNK_FL, "h", "sHrunk" }, + { NEXT3_SNAPFILE_OPEN_FL, "o", "mOunted" }, + { NEXT3_SNAPFILE_TAGGED_FL, "t", "Tagged" }, + { 0, NULL, NULL } +}; + +#endif +#endif void print_flags (FILE * f, unsigned long flags, unsigned options) { +#ifdef EXT2FS_SNAPSHOT_CTL + struct flags_name *array = ((options & PFOPT_SNAPSHOT_X) ? + snapshot_X_flags_array : + ((options & PFOPT_SNAPSHOT) ? + snapshot_flags_array : flags_array)); +#endif int long_opt = (options & PFOPT_LONG); struct flags_name *fp; int first = 1; +#ifdef EXT2FS_SNAPSHOT_CTL + for (fp = array; fp->flag != 0; fp++) { +#else for (fp = flags_array; fp->flag != 0; fp++) { +#endif if (flags & fp->flag) { if (long_opt) { if (first) diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c index 4054a07..89a1e2b 100644 --- a/lib/ext2fs/block.c +++ b/lib/ext2fs/block.c @@ -310,6 +310,7 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs, errcode_t retval; struct block_context ctx; int limit; + blk_t blk64; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); @@ -459,9 +460,19 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs, * Iterate over normal data blocks */ for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + if ((inode.i_flags & EXT4_SNAPFILE_FL) && + LINUX_S_ISREG(inode.i_mode) && + i < NEXT3_EXTRA_TIND_BLOCKS) + /* snapshot file extra triple indirect blocks */ + continue; + +#endif if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) { - ret |= (*ctx.func)(fs, &inode.i_block[i], - ctx.bcount, 0, i, priv_data); + blk64 = inode.i_block[i]; + ret |= (*ctx.func)(fs, &blk64, ctx.bcount, 0, i, + priv_data); + inode.i_block[i] = (blk_t) blk64; if (ret & BLOCK_ABORT) goto abort_exit; } @@ -487,6 +498,19 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs, if (ret & BLOCK_ABORT) goto abort_exit; } +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + if ((inode.i_flags & EXT4_SNAPFILE_FL) && LINUX_S_ISREG(inode.i_mode)) { + /* iterate snapshot file extra triple indirect blocks */ + for (i = 0; i < NEXT3_EXTRA_TIND_BLOCKS; i++) { + if (!inode.i_block[i] && !(flags & BLOCK_FLAG_APPEND)) + continue; + ret |= block_iterate_tind(&inode.i_block[i], + 0, EXT2_N_BLOCKS+i, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + } +#endif abort_exit: if (ret & BLOCK_CHANGED) { diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c index fbcb375..1385770 100644 --- a/lib/ext2fs/bmap.c +++ b/lib/ext2fs/bmap.c @@ -136,6 +136,10 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, struct ext2_inode inode_buf; ext2_extent_handle_t handle = 0; blk_t addr_per_block; +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + blk64_t addr_per_tind_block; + int tind; +#endif blk_t b, blk32; char *buf = 0; errcode_t retval = 0; @@ -286,7 +290,24 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, /* Triply indirect block */ block -= addr_per_block * addr_per_block; +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + tind = EXT2_TIND_BLOCK; + addr_per_tind_block = addr_per_block * addr_per_block * addr_per_block; + if (block > addr_per_tind_block) { + /* use direct blocks as extra triple indirect blocks? */ + tind = block / addr_per_tind_block; + block -= tind * addr_per_tind_block; + if (!(inode->i_flags & EXT4_SNAPFILE_FL) || + !LINUX_S_ISREG(inode->i_mode) || + tind >= NEXT3_EXTRA_TIND_BLOCKS) { + retval = EXT2_ET_BAD_BLOCK_NUM; + goto done; + } + } + b = inode_bmap(inode, tind); +#else b = inode_bmap(inode, EXT2_TIND_BLOCK); +#endif if (!b) { if (!(bmap_flags & BMAP_ALLOC)) { if (bmap_flags & BMAP_SET) @@ -298,7 +319,11 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + inode_bmap(inode, tind) = b; +#else inode_bmap(inode, EXT2_TIND_BLOCK) = b; +#endif blocks_alloc++; } retval = block_tind_bmap(fs, bmap_flags, b, block_buf, diff --git a/lib/ext2fs/dupfs.c b/lib/ext2fs/dupfs.c index a9e2a97..984fb5e 100644 --- a/lib/ext2fs/dupfs.c +++ b/lib/ext2fs/dupfs.c @@ -76,6 +76,13 @@ errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest) if (retval) goto errout; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (src->exclude_map) { + retval = ext2fs_copy_bitmap(src->exclude_map, &fs->exclude_map); + if (retval) + goto errout; + } +#endif if (src->badblocks) { retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks); if (retval) diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index afd33a9..c30cbf7 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -26,6 +26,18 @@ * Define EXT2FS_DEBUG to produce debug messages */ #undef EXT2FS_DEBUG +#define EXT2FS_SNAPSHOT_ON_DISK +#define EXT2FS_SNAPSHOT_BIG_JOURNAL +#define EXT2FS_SNAPSHOT_EXCLUDE_INODE +#define EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +#define EXT2FS_SNAPSHOT_CTL +#define EXT2FS_SNAPSHOT_HAS_SNAPSHOT +#define EXT2FS_SNAPSHOT_CHECK_LIST +#define EXT2FS_SNAPSHOT_FIX_SNAPSHOT +#define EXT2FS_SNAPSHOT_HUGE_SNAPSHOT +#define EXT2FS_SNAPSHOT_MESSAGE_BUFFER +#define EXT2FS_SNAPSHOT_CTL_OLD +#define EXT2FS_SNAPSHOT_CLEANUP /* * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files @@ -145,11 +157,19 @@ struct ext2_group_desc __u16 bg_free_inodes_count; /* Free inodes count */ __u16 bg_used_dirs_count; /* Directories count */ __u16 bg_flags; +#ifdef EXT2FS_SNAPSHOT_ON_DISK + __u32 bg_exclude_bitmap; /* Exclude bitmap block */ + __u32 bg_reserved[1]; +#else __u32 bg_reserved[2]; +#endif __u16 bg_itable_unused; /* Unused inodes count */ __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ }; +/* + * Structure of a blocks group descriptor + */ struct ext4_group_desc { __u32 bg_block_bitmap; /* Blocks bitmap block */ @@ -159,7 +179,12 @@ struct ext4_group_desc __u16 bg_free_inodes_count; /* Free inodes count */ __u16 bg_used_dirs_count; /* Directories count */ __u16 bg_flags; +#ifdef EXT2FS_SNAPSHOT_ON_DISK + __u32 bg_exclude_bitmap; /* Exclude bitmap block */ + __u32 bg_reserved[1]; +#else __u32 bg_reserved[2]; +#endif __u16 bg_itable_unused; /* Unused inodes count */ __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ __u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ @@ -169,12 +194,20 @@ struct ext4_group_desc __u16 bg_free_inodes_count_hi;/* Free inodes count MSB */ __u16 bg_used_dirs_count_hi; /* Directories count MSB */ __u16 bg_pad; +#ifdef EXT2FS_SNAPSHOT_ON_DISK + __u32 bg_exclude_bitmap_hi; /* Exclude bitmap block MSB */ + __u32 bg_reserved2[2]; +#else __u32 bg_reserved2[3]; +#endif }; #define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */ #define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */ #define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ +#ifdef EXT2FS_SNAPSHOT_ON_DISK +#define EXT2_BG_EXCLUDE_UNINIT 0x0008 /* Exclude bitmap not initialized */ +#endif /* * Data structures used by the directory indexing feature @@ -247,6 +280,19 @@ struct ext2_dx_countlimit { #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT +/* + * Snapshot files have different indirection mapping that can map up to 2^32 + * logical blocks, so they can cover the mapped filesystem block address space. + * Next3 must use either 4K or 8K blocks (depending on PAGE_SIZE). + * With 8K blocks, 1 triple indirect block maps 2^33 logical blocks. + * With 4K blocks (the system default), each triple indirect block maps 2^30 + * logical blocks, so 4 triple indirect blocks map 2^32 logical blocks. + * Snapshot files in small filesystems (<= 4G), use only 1 double indirect + * block to map the entire filesystem. + */ +#define NEXT3_EXTRA_TIND_BLOCKS 3 +#endif /* * Inode flags @@ -322,6 +368,40 @@ struct ext4_new_group_input { #define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) #define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input) #define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input) +#ifdef EXT2FS_SNAPSHOT_CTL +#define EXT2_IOC_GETSNAPFLAGS _IOR('f', 13, long) +#define EXT2_IOC_SETSNAPFLAGS _IOW('f', 14, long) + +/* + * Snapshot status/control flags for lssnap/chsnap. + * The flags below must appear in the same order as they do in ext4 dynamic + * inode state flags enum. GETSNAPFLAGS ioctl shifts them down to lower 8 bits. + */ +enum { + EXT4_SNAPSHOT_LIST, /* snapshot is on list (S) */ + EXT4_SNAPSHOT_ENABLED, /* snapshot is enabled (n) */ + EXT4_SNAPSHOT_ACTIVE, /* snapshot is active (a) */ + EXT4_SNAPSHOT_INUSE, /* snapshot is in-use (p) */ + EXT4_SNAPSHOT_DELETED, /* snapshot is deleted (s) */ + EXT4_SNAPSHOT_SHRUNK, /* snapshot was shrunk (h) */ + EXT4_SNAPSHOT_OPEN, /* snapshot is mounted (o) */ + EXT4_SNAPSHOT_TAGGED, /* snapshot is tagged (t) */ +}; + + +#ifdef EXT2FS_SNAPSHOT_CTL_OLD +/* Old snapshot flags for backward compatibility with next3 */ +#define NEXT3_SNAPFILE_LIST_FL 0x00000100 /* snapshot is on list */ +#define NEXT3_SNAPFILE_ENABLED_FL 0x00000200 /* snapshot is enabled */ +#define NEXT3_SNAPFILE_ACTIVE_FL 0x00000400 /* snapshot is active */ +#define NEXT3_SNAPFILE_INUSE_FL 0x00000800 /* snapshot is in-use */ +#define NEXT3_SNAPFILE_DELETED_FL 0x04000000 /* snapshot is deleted */ +#define NEXT3_SNAPFILE_SHRUNK_FL 0x08000000 /* snapshot is shrunk */ +#define NEXT3_SNAPFILE_OPEN_FL 0x10000000 /* snapshot is mounted */ +#define NEXT3_SNAPFILE_TAGGED_FL 0x20000000 /* snapshot is tagged */ + +#endif +#endif /* * Structure of an inode on the disk @@ -428,7 +508,11 @@ struct ext2_inode_large { #define i_size_high i_dir_acl #if defined(__KERNEL__) || defined(__linux__) +#ifdef EXT2FS_SNAPSHOT_ON_DISK +#define i_next_snapshot osd1.linux1.l_i_version +#else #define i_reserved1 osd1.linux1.l_i_reserved1 +#endif #define i_frag osd2.linux2.l_i_frag #define i_fsize osd2.linux2.l_i_fsize #define i_uid_low i_uid @@ -644,6 +728,18 @@ struct ext2_super_block { */ #define EXT3_JNL_BACKUP_BLOCKS 1 +#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL +/* + * A 'big' journal needs to accomodate extra snapshot COW credits. + * On mke2fs -O snapshots, we create a journal to hold maximum COW credits. + * On tune2fs -O snapshots, we check if journal can hold average COW credits. + */ +#define EXT3_DEF_JOURNAL_BLOCKS 32768 +#define EXT4_AVG_COW_CREDITS 16 +#define EXT4_MAX_COW_CREDITS 24 +#define EXT4_BIG_JOURNAL_BLOCKS (EXT3_DEF_JOURNAL_BLOCKS * EXT4_AVG_COW_CREDITS) + +#endif /* * Feature set definitions */ @@ -663,6 +759,9 @@ struct ext2_super_block { #define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 #define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040 #define EXT2_FEATURE_COMPAT_EXCLUDE_INODE 0x0080 +#ifdef EXT2FS_SNAPSHOT_ON_DISK +#define EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP 0x0100 +#endif #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 @@ -688,10 +787,18 @@ struct ext2_super_block { #define EXT2_FEATURE_COMPAT_SUPP 0 #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) +#ifdef EXT2FS_SNAPSHOT_ON_DISK #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\ EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#else +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#endif /* * Default values for user and/or group using reserved blocks diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index cf76562..7f0ae02 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -38,6 +38,12 @@ extern "C" { */ #define SUPERBLOCK_OFFSET 1024 #define SUPERBLOCK_SIZE 1024 +/* + * When blocksize > 2K, the space after the superblock is used as + * a buffer to record error messages (in 256 bytes records). + */ +#define MSGBUF_OFFSET (SUPERBLOCK_OFFSET+SUPERBLOCK_SIZE) +#define MSGBUF_RECLEN 256 /* * The last ext2fs revision level that this version of the library is @@ -173,7 +179,12 @@ typedef struct ext2_file *ext2_file_t; #define EXT2_FLAG_EXCLUSIVE 0x4000 #define EXT2_FLAG_SOFTSUPP_FEATURES 0x8000 #define EXT2_FLAG_NOFREE_ON_ERROR 0x10000 +#define EXT2_FLAG_64BITS 0x20000 +#define EXT2_FLAG_PRINT_PROGRESS 0x40000 #define EXT2_FLAG_DIRECT_IO 0x80000 +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +#define EXT2_FLAG_EXCLUDE_DIRTY 0x100000 +#endif /* * Special flag in the ext2 inode i_flag field that means that this is @@ -183,10 +194,11 @@ typedef struct ext2_file *ext2_file_t; /* * Flags for mkjournal - * - * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock */ -#define EXT2_MKJOURNAL_V1_SUPER 0x0000001 +#define EXT2_MKJOURNAL_V1_SUPER 0x0000001 /* create V1 superblock (deprecated) */ +#define EXT2_MKJOURNAL_LAZYINIT 0x0000002 /* don't zero journal inode before use*/ + +#define opaque_ext2_group_desc ext2_group_desc struct struct_ext2_filsys { errcode_t magic; @@ -198,10 +210,14 @@ struct struct_ext2_filsys { int fragsize; dgrp_t group_desc_count; unsigned long desc_blocks; - struct ext2_group_desc * group_desc; + struct opaque_ext2_group_desc * group_desc; int inode_blocks_per_group; ext2fs_inode_bitmap inode_map; ext2fs_block_bitmap block_map; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + ext2fs_block_bitmap exclude_map; +#endif + /* XXX FIXME-64: not 64-bit safe, but not used? */ errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino); errcode_t (*write_bitmaps)(ext2_filsys fs); @@ -503,12 +519,23 @@ typedef struct ext2_icount *ext2_icount_t; /* * Features supported by this version of the library */ +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP #define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ + EXT2_FEATURE_COMPAT_EXCLUDE_INODE|\ + EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP|\ EXT2_FEATURE_COMPAT_RESIZE_INODE|\ EXT2_FEATURE_COMPAT_DIR_INDEX|\ EXT2_FEATURE_COMPAT_EXT_ATTR) +#else +#define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ + EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ + EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ + EXT2_FEATURE_COMPAT_RESIZE_INODE|\ + EXT2_FEATURE_COMPAT_DIR_INDEX|\ + EXT2_FEATURE_COMPAT_EXT_ATTR) +#endif /* This #ifdef is temporary until compression is fully supported */ #ifdef ENABLE_COMPRESSION @@ -533,12 +560,22 @@ typedef struct ext2_icount *ext2_icount_t; EXT3_FEATURE_INCOMPAT_EXTENTS|\ EXT4_FEATURE_INCOMPAT_FLEX_BG) #endif +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT +#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ + EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\ + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\ + EXT4_FEATURE_RO_COMPAT_GDT_CSUM) +#else #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\ EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) +#endif /* * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed @@ -646,8 +683,14 @@ extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, ext2fs_generic_bitmap *dest); extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs); extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +extern errcode_t ext2fs_write_exclude_bitmap (ext2_filsys fs); +#endif extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs); extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +extern errcode_t ext2fs_read_exclude_bitmap (ext2_filsys fs); +#endif extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, const char *descr, ext2fs_block_bitmap *ret); @@ -1073,6 +1116,10 @@ extern errcode_t ext2fs_add_journal_device(ext2_filsys fs, extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags); extern int ext2fs_default_journal_size(__u64 blocks); +#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL +extern int ext2fs_big_journal_size(int factor, __u64 blocks); +extern int ext2fs_check_journal_size(ext2_filsys fs); +#endif /* openfs.c */ extern errcode_t ext2fs_open(const char *name, int flags, int superblock, @@ -1117,6 +1164,15 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, /* res_gdt.c */ extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +extern errcode_t ext2fs_create_exclude_inode(ext2_filsys fs, int flags); + +/* exclude inode creation flags */ +#define EXCLUDE_READONLY 0 /* only read exclude bitmap blocks */ +#define EXCLUDE_ALLOC 1 /* allocate missing exclude bitmap blocks */ +#define EXCLUDE_RESET 2 /* reset exclude bitmap blocks to zero */ +#define EXCLUDE_CREATE 3 /* alloc and/or reset exclude bitmap blocks */ +#endif /* swapfs.c */ extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, @@ -1212,7 +1268,8 @@ _INLINE_ errcode_t ext2fs_get_memalign(unsigned long size, if (align == 0) align = 8; - if (retval = posix_memalign((void **) ptr, align, size)) { + retval = posix_memalign((void **) ptr, align, size); + if (retval) { if (retval == ENOMEM) return EXT2_ET_NO_MEMORY; return retval; @@ -1324,6 +1381,16 @@ _INLINE_ void ext2fs_mark_bb_dirty(ext2_filsys fs) fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +/* + * Mark the exclude bitmap as dirty + */ +_INLINE_ void ext2fs_mark_exclude_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_EXCLUDE_DIRTY | EXT2_FLAG_CHANGED; +} + +#endif /* * Check to see if a filesystem's inode bitmap is dirty */ @@ -1340,6 +1407,16 @@ _INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs) return (fs->flags & EXT2_FLAG_BB_DIRTY); } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +/* + * Check to see if a filesystem's exclude bitmap is dirty + */ +_INLINE_ int ext2fs_test_exclude_dirty(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_EXCLUDE_DIRTY); +} + +#endif /* * Return the group # of a block */ diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c index 5c35bb6..9055b09 100644 --- a/lib/ext2fs/freefs.c +++ b/lib/ext2fs/freefs.c @@ -42,6 +42,10 @@ void ext2fs_free(ext2_filsys fs) ext2fs_free_block_bitmap(fs->block_map); if (fs->inode_map) ext2fs_free_inode_bitmap(fs->inode_map); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (fs->exclude_map) + ext2fs_free_inode_bitmap(fs->exclude_map); +#endif if (fs->badblocks) ext2fs_badblocks_list_free(fs->badblocks); diff --git a/lib/ext2fs/i_block.c b/lib/ext2fs/i_block.c index 822776d..f96f200 100644 --- a/lib/ext2fs/i_block.c +++ b/lib/ext2fs/i_block.c @@ -31,8 +31,15 @@ errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode, { unsigned long long b = inode->i_blocks; +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + if (!((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + /* snapshot file always supports the 'huge_file' flag */ + (inode->i_flags & EXT4_SNAPFILE_FL)) || +#else if (!(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || +#endif !(inode->i_flags & EXT4_HUGE_FILE_FL)) num_blocks *= fs->blocksize / 512; @@ -53,8 +60,15 @@ errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, { unsigned long long b = inode->i_blocks; +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + if (!((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + /* snapshot file always supports the 'huge_file' flag */ + (inode->i_flags & EXT4_SNAPFILE_FL)) || +#else if (!(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || +#endif !(inode->i_flags & EXT4_HUGE_FILE_FL)) num_blocks *= fs->blocksize / 512; @@ -74,8 +88,15 @@ errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b) { +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + if (!((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + /* snapshot file always supports the 'huge_file' flag */ + (inode->i_flags & EXT4_SNAPFILE_FL)) || +#else if (!(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || +#endif !(inode->i_flags & EXT4_HUGE_FILE_FL)) b *= fs->blocksize / 512; diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c index 4a81f4b..2909ff5 100644 --- a/lib/ext2fs/mkjournal.c +++ b/lib/ext2fs/mkjournal.c @@ -103,7 +103,7 @@ static errcode_t write_journal_file(ext2_filsys fs, char *filename, /* Open the device or journal file */ if ((fd = open(filename, O_WRONLY)) < 0) { retval = errno; - goto errout; + goto errfree; } /* Write the superblock out */ @@ -117,6 +117,9 @@ static errcode_t write_journal_file(ext2_filsys fs, char *filename, goto errout; memset(buf, 0, fs->blocksize); + if (flags & EXT2_MKJOURNAL_LAZYINIT) + goto success; + for (i = 1; i < size; i++) { ret_size = write(fd, buf, fs->blocksize); if (ret_size < 0) { @@ -126,10 +129,12 @@ static errcode_t write_journal_file(ext2_filsys fs, char *filename, if (ret_size != (int) fs->blocksize) goto errout; } - close(fd); +success: retval = 0; errout: + close(fd); +errfree: ext2fs_free_mem(&buf); return retval; } @@ -201,6 +206,7 @@ struct mkjournal_struct { blk_t goal; blk_t blk_to_zero; int zero_count; + int flags; char *buf; errcode_t err; }; @@ -232,7 +238,7 @@ static int mkjournal_proc(ext2_filsys fs, retval = 0; if (blockcnt <= 0) retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf); - else { + else if (!(es->flags & EXT2_MKJOURNAL_LAZYINIT)) { if (es->zero_count) { if ((es->blk_to_zero + es->zero_count == new_blk) && (es->zero_count < 1024)) @@ -296,6 +302,7 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, es.newblocks = 0; es.buf = buf; es.err = 0; + es.flags = flags; es.zero_count = 0; if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { @@ -388,6 +395,49 @@ int ext2fs_default_journal_size(__u64 blocks) return 32768; } +#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL +/* + * Big journal is up to X times bigger than the default journal + * to accomodate snapshot COW credits in transactions. + * journal size is restricted to 1/32 of the filesystem size + */ +int ext2fs_big_journal_size(int factor, __u64 blocks) +{ + int mega_blocks = blocks >> 20; + if (!mega_blocks) + return ext2fs_default_journal_size(blocks); + + if (mega_blocks < factor) + /* 32K/1M = 1/32 of filesystem size */ + return EXT3_DEF_JOURNAL_BLOCKS*mega_blocks; + + /* X times bigger than the default journal */ + return EXT3_DEF_JOURNAL_BLOCKS*factor; +} + +/* + * Return the number of blocks in the journal inode + */ +int ext2fs_check_journal_size(ext2_filsys fs) +{ + struct ext2_inode j_inode; + int j_blocks; + + if (!(fs->super->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL) || + !fs->super->s_journal_inum) + return 0; + + if (ext2fs_read_inode(fs, fs->super->s_journal_inum, &j_inode)) + return -1; + + /* read journal inode size */ + j_blocks = j_inode.i_size >> EXT2_BLOCK_SIZE_BITS(fs->super); + + return j_blocks; +} + +#endif /* * This function adds a journal device to a filesystem */ @@ -490,6 +540,13 @@ errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) return errno; + /* Note that we can't do lazy journal initialization for mounted + * filesystems, since the zero writing is also allocating the + * journal blocks. We could use fallocate, but not all kernels + * support that, and creating a journal on a mounted ext2 + * filesystems is extremely rare these days... Ignore it. */ + flags &= ~EXT2_MKJOURNAL_LAZYINIT; + if ((retval = write_journal_file(fs, jfile, size, flags))) goto errout; diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c index 424d867..787bd5a 100644 --- a/lib/ext2fs/res_gdt.c +++ b/lib/ext2fs/res_gdt.c @@ -217,3 +217,220 @@ out_free: return retval; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +#define ext2fs_group_desc(fs, gdp, grp) (gdp)+(grp) + +/* + * ext2fs_create_exclude_inode(): + * the exclude inode owns all the exclude bitmap blocks (one per block group) + * the exclude bitmap blocks are double indirectly linked to the exclude inode + * the exclude bitmap allocation goal is the first block of the block group + * exclude inode creation @flags: + * EXCLUDE_ALLOC (1) - allocate missing exclude bitmap blocks + * EXCLUDE_RESET (2) - reset exclude bitmap to zero + */ +errcode_t ext2fs_create_exclude_inode(ext2_filsys fs, int flags) +{ + errcode_t retval, retval2; + struct ext2_super_block *sb; + struct ext2_inode inode; + __u32 *dindir_buf, *indir_buf, *data_buf; + unsigned long long apb, inode_size; + blk_t dindir_blk, indir_blk, data_blk; + int gdt_dirty = 0, dindir_dirty = 0, inode_dirty = 0; + int indir_dirty = 0, data_dirty = 0; + int dindir_off, indir_off, grp, i, max_groups; + int create = flags & EXCLUDE_ALLOC; + int reset = flags & EXCLUDE_RESET; + + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + sb = fs->super; + + retval = ext2fs_get_array(3, fs->blocksize, &dindir_buf); + if (retval) + goto out_free; + indir_buf = (__u32 *)((char *)dindir_buf + 1*fs->blocksize); + data_buf = (__u32 *)((char *)dindir_buf + 2*fs->blocksize); + + retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO, &inode); + if (retval) + goto out_free; + +#ifdef EXCLUDE_INO_PROGRESS + printf("Reserving exclude bitmap blocks: "); +#endif + + apb = EXT2_ADDR_PER_BLOCK(sb); + if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { +#ifdef EXCLUDE_INO_DEBUG + printf("reading exclude inode dindir %u\n", dindir_blk); +#endif + retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); + if (retval) + goto out_free; + } else if (create) { + blk_t goal = sb->s_first_data_block + fs->desc_blocks + + sb->s_reserved_gdt_blocks + 2 + + fs->inode_blocks_per_group; + + retval = ext2fs_alloc_block(fs, goal, (char *)dindir_buf, &dindir_blk); + if (retval) + goto out_free; + inode.i_mode = LINUX_S_IFREG | 0600; + inode.i_links_count = 1; + inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; + ext2fs_iblk_set(fs, &inode, 1); +#ifdef EXCLUDE_INO_DEBUG + printf("allocated exclude inode dindir %u\n", dindir_blk); +#endif + dindir_dirty = inode_dirty = 1; + inode.i_ctime = fs->now ? fs->now : time(0); + } + + /* + * allocate exclude bitmaps for all existing block groups + * and allocate indirect blocks for all reserved block groups + */ + max_groups = fs->desc_blocks + sb->s_reserved_gdt_blocks; + max_groups *= EXT2_DESC_PER_BLOCK(sb); + for (grp = 0; grp < max_groups; grp++) { + struct ext2_group_desc *gd = + ext2fs_group_desc(fs, fs->group_desc, grp); + + dindir_off = grp/apb; + indir_off = grp%apb; + if (indir_off == 0) { + /* flush current indirect block */ + if (indir_dirty) { + retval = ext2fs_write_ind_block(fs, indir_blk, indir_buf); + if (retval) + goto out_dindir; + indir_dirty = 0; + } + /* read/alloc next indirect block */ + if ((indir_blk = dindir_buf[dindir_off])) { +#ifdef EXCLUDE_INO_DEBUG + printf("reading exclude inode indir %u\n", indir_blk); +#endif + retval = ext2fs_read_ind_block(fs, indir_blk, indir_buf); + if (retval) + goto out_dindir; + } else if (create) { + retval = ext2fs_alloc_block(fs, dindir_blk, (char *)indir_buf, &indir_blk); + if (retval) + goto out_dindir; + dindir_buf[dindir_off] = indir_blk; + ext2fs_iblk_add_blocks(fs, &inode, 1); +#ifdef EXCLUDE_INO_DEBUG + printf("allocated exclude inode indir %u\n", indir_blk); +#endif + dindir_dirty = inode_dirty = 1; + } + } + + if (grp >= fs->group_desc_count) + continue; + /* read/alloc exclude bitmap block */ + data_blk = indir_buf[indir_off]; + if (!data_blk && create) { + /* allocate exclude bitmap block */ + retval = ext2fs_alloc_block(fs, gd->bg_block_bitmap, + (char *)data_buf, &data_blk); + if (retval) + goto out_dindir; + indir_buf[indir_off] = data_blk; + ext2fs_iblk_add_blocks(fs, &inode, 1); +#ifdef EXCLUDE_INO_DEBUG + printf("allocated exclude bitmap block %u\n", data_blk); +#endif + indir_dirty = inode_dirty = 1; + } else if (data_blk && reset) { + /* reset exclude bitmap block */ +#ifdef EXCLUDE_INO_DEBUG + printf("reading exclude bitmap block %u\n", data_blk); +#endif + retval = io_channel_read_blk(fs->io, data_blk, 1, + data_buf); + if (retval) + goto out_dindir; + /* zero data block */ + for (i = 0; i < apb; i++) { + if (!data_buf[i]) + continue; + data_buf[i] = 0; + data_dirty = 1; + } + if (data_dirty) { + retval = io_channel_write_blk(fs->io, data_blk, + 1, data_buf); + if (retval) + goto out_dindir; + data_dirty = 0; + } + } + if (!gd->bg_exclude_bitmap) { + gd->bg_exclude_bitmap = data_blk; + ext2fs_group_desc_csum_set(fs, grp); + gdt_dirty = 1; + } +#ifdef EXCLUDE_INO_PROGRESS + printf("\b\b\b\b\b\b\b\b\b\b\b%5d/%5d", grp, + fs->group_desc_count); +#endif + } +#ifdef EXCLUDE_INO_PROGRESS + printf("\b\b\b\b\b\b\b\b\b\b\bdone \n"); +#endif + + /* exclude bitmap was reset to zero - clear fix_exclude flag */ + if (sb->s_flags & EXT2_FLAGS_FIX_EXCLUDE) { + sb->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE; + ext2fs_mark_super_dirty(fs); + } + +out_dindir: + if (indir_dirty) { + retval2 = ext2fs_write_ind_block(fs, indir_blk, indir_buf); + if (!retval) + retval = retval2; + } + if (dindir_dirty) { + retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); + if (!retval) + retval = retval2; + } +out_inode: + if (inode_dirty) { + inode_size = fs->group_desc_count + apb + EXT2_NDIR_BLOCKS; + inode_size *= fs->blocksize; + inode.i_size = inode_size & 0xFFFFFFFF; + inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); + retval2 = ext2fs_write_new_inode(fs, EXT2_EXCLUDE_INO, &inode); + if (!retval) + retval = retval2; + /* need to write out block bitmaps and group descriptors */ + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + } + if (gdt_dirty) { + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + ext2fs_mark_super_dirty(fs); + } + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP)) { + /* set exclude_bitmap along with exclude_inode */ + fs->super->s_feature_compat |= + EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP; + ext2fs_mark_super_dirty(fs); + } +#ifdef EXCLUDE_INO_DEBUG + printf("inode.i_blocks = %u, i_size = %u\n", + inode.i_blocks, inode.i_size); +#endif +out_free: + ext2fs_free_mem(&dindir_buf); + return retval; +} + +#endif diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c index 4e77a8f..c681660 100644 --- a/lib/ext2fs/rw_bitmaps.c +++ b/lib/ext2fs/rw_bitmaps.c @@ -25,19 +25,47 @@ #include "ext2_fs.h" #include "ext2fs.h" + +#define blk64_t blk_t +#define __u64 __u32 +#define io_channel_read_blk64 io_channel_read_blk +#define io_channel_write_blk64 io_channel_write_blk +#define ext2fs_get_block_bitmap_range2 ext2fs_get_block_bitmap_range +#define ext2fs_set_block_bitmap_range2 ext2fs_set_block_bitmap_range +#define ext2fs_block_bitmap_loc(fs, group) \ + fs->group_desc[group].bg_block_bitmap +#define ext2fs_exclude_bitmap_loc(fs, group) \ + fs->group_desc[group].bg_exclude_bitmap +#define ext2fs_inode_bitmap_loc(fs, group) \ + fs->group_desc[group].bg_inode_bitmap +#define ext2fs_bg_flags_test(fs, group, bg_flag) \ + fs->group_desc[group].bg_flags & bg_flag +#define ext2fs_blocks_count(sb) sb->s_blocks_count + +#include "ext2_fs.h" +#include "ext2fs.h" #include "e2image.h" +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block, + int do_exclude) +#else static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) +#endif { dgrp_t i; unsigned int j; int block_nbytes, inode_nbytes; unsigned int nbits; errcode_t retval; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + char *block_buf, *inode_buf, *exclude_buf; +#else char *block_buf, *inode_buf; +#endif int csum_flag = 0; - blk_t blk; - blk_t blk_itr = fs->super->s_first_data_block; + blk64_t blk; + blk64_t blk_itr = fs->super->s_first_data_block; ext2_ino_t ino_itr = 1; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); @@ -45,6 +73,12 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (!EXT2_HAS_COMPAT_FEATURE(fs->super, + EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP)) + do_exclude = 0; + +#endif if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) csum_flag = 1; @@ -58,6 +92,16 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) return retval; memset(block_buf, 0xff, fs->blocksize); } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (do_exclude) { + block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize, + &exclude_buf); + if (retval) + return retval; + memset(exclude_buf, 0xff, fs->blocksize); + } +#endif if (do_inode) { inode_nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); @@ -69,34 +113,67 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) } for (i = 0; i < fs->group_desc_count; i++) { +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (!do_block && !do_exclude) +#else if (!do_block) +#endif goto skip_block_bitmap; - if (csum_flag && fs->group_desc[i].bg_flags & - EXT2_BG_BLOCK_UNINIT) + if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) + ) goto skip_this_block_bitmap; - retval = ext2fs_get_block_bitmap_range(fs->block_map, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (do_block) + retval = ext2fs_get_block_bitmap_range2(fs->block_map, + blk_itr, block_nbytes << 3, block_buf); + if (retval) + return retval; + + if (do_exclude) + retval = ext2fs_get_block_bitmap_range2(fs->exclude_map, + blk_itr, block_nbytes << 3, exclude_buf); +#else + retval = ext2fs_get_block_bitmap_range2(fs->block_map, blk_itr, block_nbytes << 3, block_buf); +#endif if (retval) return retval; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (do_block && i == fs->group_desc_count - 1) { +#else if (i == fs->group_desc_count - 1) { +#endif /* Force bitmap padding for the last group */ - nbits = ((fs->super->s_blocks_count - - fs->super->s_first_data_block) - % EXT2_BLOCKS_PER_GROUP(fs->super)); + nbits = ((ext2fs_blocks_count(fs->super) + - (__u64) fs->super->s_first_data_block) + % (__u64) EXT2_BLOCKS_PER_GROUP(fs->super)); if (nbits) for (j = nbits; j < fs->blocksize * 8; j++) ext2fs_set_bit(j, block_buf); } - blk = fs->group_desc[i].bg_block_bitmap; + blk = ext2fs_block_bitmap_loc(fs, i); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (do_block && blk) { +#else if (blk) { - retval = io_channel_write_blk(fs->io, blk, 1, - block_buf); +#endif + retval = io_channel_write_blk64(fs->io, blk, 1, + block_buf); if (retval) return EXT2_ET_BLOCK_BITMAP_WRITE; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + blk = ext2fs_exclude_bitmap_loc(fs, i); + if (do_exclude && blk) { + retval = io_channel_write_blk64(fs->io, blk, 1, + exclude_buf); + if (retval) + return EXT2_ET_BLOCK_BITMAP_WRITE; + } +#endif skip_this_block_bitmap: blk_itr += block_nbytes << 3; skip_block_bitmap: @@ -104,8 +181,8 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) if (!do_inode) continue; - if (csum_flag && fs->group_desc[i].bg_flags & - EXT2_BG_INODE_UNINIT) + if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) + ) goto skip_this_inode_bitmap; retval = ext2fs_get_inode_bitmap_range(fs->inode_map, @@ -113,9 +190,9 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) if (retval) return retval; - blk = fs->group_desc[i].bg_inode_bitmap; + blk = ext2fs_inode_bitmap_loc(fs, i); if (blk) { - retval = io_channel_write_blk(fs->io, blk, 1, + retval = io_channel_write_blk64(fs->io, blk, 1, inode_buf); if (retval) return EXT2_ET_INODE_BITMAP_WRITE; @@ -135,10 +212,19 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) return 0; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block, + int do_exclude) +#else static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) +#endif { dgrp_t i; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + char *block_bitmap = 0, *inode_bitmap = 0, *exclude_bitmap = 0; +#else char *block_bitmap = 0, *inode_bitmap = 0; +#endif char *buf; errcode_t retval; int block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; @@ -146,9 +232,9 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) int csum_flag = 0; int do_image = fs->flags & EXT2_FLAG_IMAGE_FILE; unsigned int cnt; - blk_t blk; - blk_t blk_itr = fs->super->s_first_data_block; - blk_t blk_cnt; + blk64_t blk; + blk64_t blk_itr = fs->super->s_first_data_block; + blk64_t blk_cnt; ext2_ino_t ino_itr = 1; ext2_ino_t ino_cnt; @@ -156,6 +242,12 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) fs->write_bitmaps = ext2fs_write_bitmaps; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (!EXT2_HAS_COMPAT_FEATURE(fs->super, + EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP)) + do_exclude = 0; + +#endif if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) csum_flag = 1; @@ -180,7 +272,29 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) if (retval) goto cleanup; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + } + if (do_exclude) { + if (fs->exclude_map) + ext2fs_free_block_bitmap(fs->exclude_map); + strcpy(buf, "exclude bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->exclude_map); + if (retval) + goto cleanup; + if (do_image) + retval = ext2fs_get_mem(fs->blocksize, &exclude_bitmap); + else + retval = ext2fs_get_memalign((unsigned) block_nbytes, + fs->blocksize, + &exclude_bitmap); + if (retval) + goto cleanup; + } + if (!do_block && !do_exclude) +#else } else +#endif block_nbytes = 0; if (do_inode) { if (fs->inode_map) @@ -202,7 +316,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) blk = (fs->image_header->offset_inodemap / fs->blocksize); ino_cnt = fs->super->s_inodes_count; while (inode_nbytes > 0) { - retval = io_channel_read_blk(fs->image_io, blk++, + retval = io_channel_read_blk64(fs->image_io, blk++, 1, inode_bitmap); if (retval) goto cleanup; @@ -219,17 +333,24 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) } blk = (fs->image_header->offset_blockmap / fs->blocksize); - blk_cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * + blk_cnt = (blk64_t)EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; while (block_nbytes > 0) { - retval = io_channel_read_blk(fs->image_io, blk++, +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (do_exclude) { + retval = EXT2_ET_BLOCK_BITMAP_READ; + goto cleanup; + } + +#endif + retval = io_channel_read_blk64(fs->image_io, blk++, 1, block_bitmap); if (retval) goto cleanup; cnt = fs->blocksize << 3; if (cnt > blk_cnt) cnt = blk_cnt; - retval = ext2fs_set_block_bitmap_range(fs->block_map, + retval = ext2fs_set_block_bitmap_range2(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; @@ -242,13 +363,13 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) for (i = 0; i < fs->group_desc_count; i++) { if (block_bitmap) { - blk = fs->group_desc[i].bg_block_bitmap; - if (csum_flag && fs->group_desc[i].bg_flags & - EXT2_BG_BLOCK_UNINIT && + blk = ext2fs_block_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { - retval = io_channel_read_blk(fs->io, blk, + retval = io_channel_read_blk64(fs->io, blk, -block_nbytes, block_bitmap); if (retval) { retval = EXT2_ET_BLOCK_BITMAP_READ; @@ -257,20 +378,47 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) } else memset(block_bitmap, 0, block_nbytes); cnt = block_nbytes << 3; - retval = ext2fs_set_block_bitmap_range(fs->block_map, + retval = ext2fs_set_block_bitmap_range2(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + } + if (exclude_bitmap) { + blk = ext2fs_exclude_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && + ext2fs_group_desc_csum_verify(fs, i)) + blk = 0; + if (blk) { + retval = io_channel_read_blk64(fs->io, blk, + -block_nbytes, exclude_bitmap); + if (retval) { + retval = EXT2_ET_BLOCK_BITMAP_READ; + goto cleanup; + } + } else + memset(exclude_bitmap, 0, block_nbytes); + cnt = block_nbytes << 3; + retval = ext2fs_set_block_bitmap_range2(fs->exclude_map, + blk_itr, cnt, exclude_bitmap); + if (retval) + goto cleanup; + } + if (block_nbytes) + blk_itr += block_nbytes << 3; +#else blk_itr += block_nbytes << 3; } +#endif if (inode_bitmap) { - blk = fs->group_desc[i].bg_inode_bitmap; - if (csum_flag && fs->group_desc[i].bg_flags & - EXT2_BG_INODE_UNINIT && + blk = ext2fs_inode_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { - retval = io_channel_read_blk(fs->io, blk, + retval = io_channel_read_blk64(fs->io, blk, -inode_nbytes, inode_bitmap); if (retval) { retval = EXT2_ET_INODE_BITMAP_READ; @@ -291,6 +439,10 @@ success_cleanup: ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (exclude_bitmap) + ext2fs_free_mem(&exclude_bitmap); +#endif return 0; cleanup: @@ -306,6 +458,10 @@ cleanup: ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (exclude_bitmap) + ext2fs_free_mem(&exclude_bitmap); +#endif if (buf) ext2fs_free_mem(&buf); return retval; @@ -313,39 +469,88 @@ cleanup: errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs) { +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + return read_bitmaps(fs, 1, 0, 0); +#else return read_bitmaps(fs, 1, 0); +#endif } errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) { +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + return read_bitmaps(fs, 0, 1, 0); +#else return read_bitmaps(fs, 0, 1); +#endif +} + +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +errcode_t ext2fs_read_exclude_bitmap (ext2_filsys fs) +{ + return read_bitmaps(fs, 0, 0, 1); } +#endif errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) { +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + return write_bitmaps(fs, 1, 0, 0); +#else return write_bitmaps(fs, 1, 0); +#endif } errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) { +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + return write_bitmaps(fs, 0, 1, 0); +#else return write_bitmaps(fs, 0, 1); +#endif } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP +errcode_t ext2fs_write_exclude_bitmap (ext2_filsys fs) +{ + return write_bitmaps(fs, 0, 0, 1); +} + +#endif errcode_t ext2fs_read_bitmaps(ext2_filsys fs) { +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (fs->inode_map && fs->block_map && fs->exclude_map) +#else if (fs->inode_map && fs->block_map) +#endif return 0; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + return read_bitmaps(fs, !fs->inode_map, !fs->block_map, !fs->exclude_map); +#else return read_bitmaps(fs, !fs->inode_map, !fs->block_map); +#endif } errcode_t ext2fs_write_bitmaps(ext2_filsys fs) { int do_inode = fs->inode_map && ext2fs_test_ib_dirty(fs); int do_block = fs->block_map && ext2fs_test_bb_dirty(fs); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + int do_exclude = fs->exclude_map && ext2fs_test_exclude_dirty(fs); +#endif +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (!do_inode && !do_block && !do_exclude) +#else if (!do_inode && !do_block) +#endif return 0; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + return write_bitmaps(fs, do_inode, do_block, do_exclude); +#else return write_bitmaps(fs, do_inode, do_block); +#endif } diff --git a/misc/Makefile.in b/misc/Makefile.in index def66f0..591053f 100644 --- a/misc/Makefile.in +++ b/misc/Makefile.in @@ -32,7 +32,7 @@ SMANPAGES= tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \ FMANPAGES= mke2fs.conf.5 UPROGS= chattr lsattr @UUID_CMT@ uuidgen -UMANPAGES= chattr.1 lsattr.1 @UUID_CMT@ uuidgen.1 +UMANPAGES= chattr.1 lsattr.1 chsnap.1 lssnap.1 @UUID_CMT@ uuidgen.1 LPROGS= @E2INITRD_PROG@ @@ -370,6 +370,14 @@ lsattr.1: $(DEP_SUBSTITUTE) $(srcdir)/lsattr.1.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/lsattr.1.in lsattr.1 +chsnap.1: $(DEP_SUBSTITUTE) $(srcdir)/chsnap.1.in + $(E) " SUBST $@" + $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/chsnap.1.in chsnap.1 + +lssnap.1: $(DEP_SUBSTITUTE) $(srcdir)/lssnap.1.in + $(E) " SUBST $@" + $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/lssnap.1.in lssnap.1 + uuidgen.1: $(DEP_SUBSTITUTE) $(srcdir)/uuidgen.1.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuidgen.1.in uuidgen.1 @@ -423,6 +431,12 @@ install: all $(SMANPAGES) $(UMANPAGES) installdirs echo " INSTALL $(libdir)/$$i"; \ $(INSTALL_PROGRAM) $$i $(DESTDIR)$(libdir)/$$i; \ done + $(Q) $(INSTALL_PROGRAM) lsattr \ + $(DESTDIR)$(root_sbindir)/lssnap + $(Q) $(INSTALL_PROGRAM) chattr \ + $(DESTDIR)$(root_sbindir)/chsnap + $(Q) $(INSTALL_PROGRAM) $(srcdir)/../contrib/e4snapshot \ + $(DESTDIR)$(root_sbindir)/e4snapshot $(Q) for i in $(SMANPAGES); do \ for j in $(COMPRESS_EXT); do \ $(RM) -f $(DESTDIR)$(man8dir)/$$i.$$j; \ @@ -517,7 +531,7 @@ uninstall: for i in $(UMANPAGES); do \ $(RM) -f $(DESTDIR)$(man1dir)/$$i; \ done - for i in $(FINDFS_LINK) e2label ; do \ + for i in $(FINDFS_LINK) e2label lssnap chsnap e4snapshot ; do \ $(RM) -f $(DESTDIR)$(root_sbindir)/$$i; \ done for i in $(FMANPAGES); do \ diff --git a/misc/chattr.c b/misc/chattr.c index de33b08..a28ed39 100644 --- a/misc/chattr.c +++ b/misc/chattr.c @@ -55,6 +55,10 @@ #include "nls-enable.h" static const char * program_name = "chattr"; +#ifdef EXT2FS_SNAPSHOT_CTL + +static int chsnap; +#endif static int add; static int rem; @@ -81,6 +85,18 @@ static unsigned long sf; static void usage(void) { +#ifdef EXT2FS_SNAPSHOT_CTL + if (chsnap) { + fprintf(stderr, +#ifdef EXT2FS_SNAPSHOT_CTL_OLD + _("Usage: %s [-X] [-+=Sn] snapshot files...\n"), +#else + _("Usage: %s [-+=Sn] snapshot files...\n"), +#endif + program_name); + exit(1); + } +#endif fprintf(stderr, _("Usage: %s [-RVf] [-+=AacDdeijsSu] [-v version] files...\n"), program_name); @@ -92,7 +108,11 @@ struct flags_char { char optchar; }; +#ifdef EXT2FS_SNAPSHOT_CTL +static const struct flags_char ext2_flags_array[] = { +#else static const struct flags_char flags_array[] = { +#endif { EXT2_NOATIME_FL, 'A' }, { EXT2_SYNC_FL, 'S' }, { EXT2_DIRSYNC_FL, 'D' }, @@ -106,9 +126,32 @@ static const struct flags_char flags_array[] = { { EXT2_UNRM_FL, 'u' }, { EXT2_NOTAIL_FL, 't' }, { EXT2_TOPDIR_FL, 'T' }, +#ifdef EXT2FS_SNAPSHOT_CTL + { EXT4_SNAPFILE_FL, 'x' }, +#endif + { 0, 0 } +}; + +#ifdef EXT2FS_SNAPSHOT_CTL +static const struct flags_char *flags_array = ext2_flags_array; + +/* Snapshot dynamic state flags */ +static struct flags_char snapshot_flags_array[] = { + { 1UL<<EXT4_SNAPSHOT_LIST, 'S' }, + { 1UL<<EXT4_SNAPSHOT_ENABLED, 'n' }, + { 0, 0 } +}; + +#ifdef EXT2FS_SNAPSHOT_CTL_OLD +/* Old snapshot flags for backward compatibility with next3 */ +static struct flags_char snapshot_X_flags_array[] = { + { NEXT3_SNAPFILE_LIST_FL, 'S' }, + { NEXT3_SNAPFILE_ENABLED_FL, 'n' }, { 0, 0 } }; +#endif +#endif static unsigned long get_flag(char c) { const struct flags_char *fp; @@ -131,6 +174,12 @@ static int decode_arg (int * i, int argc, char ** argv) { case '-': for (p = &argv[*i][1]; *p; p++) { +#ifdef EXT2FS_SNAPSHOT_CTL_OLD + if (*p == 'X') { + flags_array = snapshot_X_flags_array; + continue; + } +#endif if (*p == 'R') { recursive = 1; continue; @@ -193,6 +242,9 @@ static int change_attributes(const char * name) unsigned long flags; STRUCT_STAT st; int extent_file = 0; +#ifdef EXT2FS_SNAPSHOT_CTL + int ret; +#endif if (LSTAT (name, &st) == -1) { if (!silent) @@ -201,7 +253,15 @@ static int change_attributes(const char * name) return -1; } +#ifdef EXT2FS_SNAPSHOT_CTL + if (chsnap) + ret = fgetsnapflags (name, &flags); + else + ret = fgetflags (name, &flags); + if (ret == -1) { +#else if (fgetflags(name, &flags) == -1) { +#endif if (!silent) com_err(program_name, errno, _("while reading flags on %s"), name); @@ -222,7 +282,15 @@ static int change_attributes(const char * name) print_flags (stdout, sf, 0); printf ("\n"); } +#ifdef EXT2FS_SNAPSHOT_CTL + if (chsnap) + ret = fsetsnapflags (name, sf); + else + ret = fsetflags (name, sf); + if (ret == -1) +#else if (fsetflags (name, sf) == -1) +#endif perror (name); } else { if (rem) @@ -243,7 +311,15 @@ static int change_attributes(const char * name) } if (!S_ISDIR(st.st_mode)) flags &= ~EXT2_DIRSYNC_FL; +#ifdef EXT2FS_SNAPSHOT_CTL + if (chsnap) + ret = fsetsnapflags (name, flags); + else + ret = fsetflags (name, flags); + if (ret == -1) { +#else if (fsetflags(name, flags) == -1) { +#endif if (!silent) { com_err(program_name, errno, _("while setting flags on %s"), @@ -303,6 +379,13 @@ int main (int argc, char ** argv) #endif if (argc && *argv) program_name = *argv; +#ifdef EXT2FS_SNAPSHOT_CTL + i = strlen(program_name); + if (i >= 6 && !strcmp(program_name + i - 6, "chsnap")) { + flags_array = snapshot_flags_array; + chsnap = 1; + } +#endif i = 1; while (i < argc && !end_arg) { /* '--' arg should end option processing */ diff --git a/misc/chsnap.1.in b/misc/chsnap.1.in new file mode 100644 index 0000000..ce3b09f --- /dev/null +++ b/misc/chsnap.1.in @@ -0,0 +1,99 @@ +.\" -*- nroff -*- +.TH CHSNAP 1 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@" +.SH NAME +chsnap \- change snapshot file attributes on a Linux fourth extended file system +.SH SYNOPSIS +.B chsnap +[ +.B \-RVf +] +[ +.B \-v +.I version +] +[ +.I mode +] +.I files... +.SH DESCRIPTION +.B chsnap +changes the snapshot file attributes on a Linux fourth extended file system. +.PP +The format of a symbolic mode is +-=[Sn]. +.PP +The operator `+' causes the selected attributes to be added to the +existing attributes of the files; `-' causes them to be removed; and +`=' causes them to be the only attributes that the files have. +.PP +The letters `Sn' select the new attributes for the files: +on snapshot list (S) and mount access enabled (n). +.PP +The following attributes are read-only, and may be listed by +.BR lssnap (1) +but not modified by chsnap: +active (a), in use by previous snapshot (p), request to be removed from the +list (s), shrunk (h), open for read (o) and tagged (t). +.SH OPTIONS +.TP +.B \-R +Recursively change attributes of snapshot directories and their contents. +.TP +.B \-V +Be verbose with chsnap's output and print the program version. +.TP +.B \-f +Suppress most error messages. +.TP +.BI \-v " version" +Set the snapshot file's version/generation number. +.SH ATTRIBUTES +A snapshot file with the 'S' attribute set is on the snapshot list. +Setting the attribute on a new snapshot file means requesting to add it +to the head of the list (i.e. snapshot take). +Resetting the attribute on a snapshot file means requesting to remove it +from the list (i.e. snapshot delete). +.PP +A snapshot file with the 'n' attribute set can be mounted via a loop device. +The snapshot file's size is the size of the snapshot'ed file system. +.PP +The 'a' attribute indicates the active snapshot, which is the last snapshot +taken. +.PP +The 'p' attribute indicates that the snapshot file is in use by a previous +snapshot, which means that an older snapshot is enabled for mount. +.PP +The 's' attribute indicates that the snapshot was deleted. It may not have +been removed from the list yet, but it cannot be mounted anymore and it will +be removed from the list as soon as no older snapshots depend on it. +.PP +The 'h' attribute indicates that the snapshot was deleted and shrunk. +Shrinking a snapshot file means freeing blocks, which are not referenced +by older snapshots. +.PP +The 'o' attribute indicates that the snapshot is open for read, mostly used +as indication that the snapshot is mounted via a loop device. +.PP +The 't' attribute indicates that the snapshot is tagged. This attribute is +reserved for future use and has no special meaning at the moment. +.PP +.SH AUTHOR +.B chsnap +was copied from +.B chattr +by Amir Goldstein <amir73il@users.sf.net>. +.B chattr +was written by Remy Card <Remy.Card@linux.org>. It is currently being +maintained by Theodore Ts'o <tytso@alum.mit.edu>. +.SH BUGS AND LIMITATIONS +The snapshot file attributes can only be set on snapshot files, which are +files created inside a snapshots directory. +A snapshots directory is indicated by setting the 'x' flag via +.BR chattr (1). +Snapshot file are only supported by the ext4 filesystem when the experimental +snapshot support feature is enabled. +.SH AVAILABILITY +.B chsnap +is part of the e2fsprogs package and is available from +http://e2fsprogs.sourceforge.net. +.SH SEE ALSO +.BR lssnap (1) diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c index 5e0a8a2..65b8ce6 100644 --- a/misc/dumpe2fs.c +++ b/misc/dumpe2fs.c @@ -216,6 +216,17 @@ static void list_desc (ext2_filsys fs) print_bg_rel_offset(fs, fs->group_desc[i].bg_block_bitmap, 0, first_block, last_block); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP) { + fputs(_(", Exclude bitmap at "), stdout); + print_number(fs->group_desc[i].bg_exclude_bitmap); + print_bg_rel_offset(fs, + fs->group_desc[i].bg_exclude_bitmap, 0, + first_block, last_block); + } + +#endif fputs(_(", Inode bitmap at "), stdout); print_number(fs->group_desc[i].bg_inode_bitmap); print_bg_rel_offset(fs, fs->group_desc[i].bg_inode_bitmap, 0, diff --git a/misc/e2image.c b/misc/e2image.c index 83c1cca..1db8069 100644 --- a/misc/e2image.c +++ b/misc/e2image.c @@ -536,6 +536,9 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) } } else { if ((inode.i_flags & EXT4_EXTENTS_FL) || +#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT + (inode.i_flags & EXT4_SNAPFILE_FL) || +#endif inode.i_block[EXT2_IND_BLOCK] || inode.i_block[EXT2_DIND_BLOCK] || inode.i_block[EXT2_TIND_BLOCK]) { diff --git a/misc/lsattr.c b/misc/lsattr.c index 15b17ad..143aa78 100644 --- a/misc/lsattr.c +++ b/misc/lsattr.c @@ -70,7 +70,11 @@ static int generation_opt; static void usage(void) { +#ifdef EXT2FS_SNAPSHOT_CTL_OLD + fprintf(stderr, _("Usage: %s [-XRVadlv] [files...]\n"), program_name); +#else fprintf(stderr, _("Usage: %s [-RVadlv] [files...]\n"), program_name); +#endif exit(1); } @@ -78,8 +82,18 @@ static int list_attributes (const char * name) { unsigned long flags; unsigned long generation; +#ifdef EXT2FS_SNAPSHOT_CTL + int ret; + + if (pf_options & PFOPT_SNAPSHOT) + ret = fgetsnapflags (name, &flags); + else + ret = fgetflags (name, &flags); + if (ret == -1) { +#else if (fgetflags (name, &flags) == -1) { +#endif com_err (program_name, errno, _("While reading flags on %s"), name); return -1; @@ -169,9 +183,26 @@ int main (int argc, char ** argv) #endif if (argc && *argv) program_name = *argv; +#ifdef EXT2FS_SNAPSHOT_CTL + i = strlen(program_name); + if (i >= 6 && !strcmp(program_name + i - 6, "lssnap")) + pf_options |= PFOPT_SNAPSHOT; + +#endif +#ifdef EXT2FS_SNAPSHOT_CTL_OLD + while ((c = getopt (argc, argv, "XRVadlv")) != EOF) +#else while ((c = getopt (argc, argv, "RVadlv")) != EOF) +#endif switch (c) { +#ifdef EXT2FS_SNAPSHOT_CTL_OLD + case 'X': + /* for backward compatibility with next3 */ + pf_options &= ~PFOPT_SNAPSHOT; + pf_options |= PFOPT_SNAPSHOT_X; + break; +#endif case 'R': recursive = 1; break; @@ -185,7 +216,11 @@ int main (int argc, char ** argv) dirs_opt = 1; break; case 'l': +#ifdef EXT2FS_SNAPSHOT_CTL + pf_options |= PFOPT_LONG; +#else pf_options = PFOPT_LONG; +#endif break; case 'v': generation_opt = 1; diff --git a/misc/lssnap.1.in b/misc/lssnap.1.in new file mode 100644 index 0000000..73fcb65 --- /dev/null +++ b/misc/lssnap.1.in @@ -0,0 +1,49 @@ +.\" -*- nroff -*- +.TH LSSNAP 1 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@" +.SH NAME +lssnap \- list snapshot file attributes on a Linux fourth extended file system +.SH SYNOPSIS +.B lssnap +[ +.B \-RVadv +] +[ +.I files... +] +.SH DESCRIPTION +.B lssnap +lists the snapshot file attributes on a fourth extended file system. See +.BR chsnap (1) +for a description of the snapshot file attributes and what they mean. +.SH OPTIONS +.TP +.B \-R +Recursively list attributes of snapshot directories and their contents. +.TP +.B \-V +Display the program version. +.TP +.B \-a +List all snapshot files in directories, including files that start with `.'. +.TP +.B \-d +List directories like other files, rather than listing their contents. +.TP +.B \-v +List the snapshot file's version/generation number. +.SH AUTHOR +.B lssnap +was copied from +.B lsattr +by Amir Goldstein <amir73il@users.sf.net>. +.B lsattr +was written by Remy Card <Remy.Card@linux.org>. It is currently being +maintained by Theodore Ts'o <tytso@alum.mit.edu>. +.SH BUGS +There are none :-). +.SH AVAILABILITY +.B lssnap +is part of the e2fsprogs package and is available from +http://e2fsprogs.sourceforge.net. +.SH SEE ALSO +.BR chsnap (1) diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in index c587d81..8402d3d 100644 --- a/misc/mke2fs.8.in +++ b/misc/mke2fs.8.in @@ -232,7 +232,15 @@ This speeds up filesystem initialization noticeably, but it requires the kernel to finish initializing the filesystem in the background when the filesystem is first mounted. If the option value is omitted, it defaults to 1 to -enable lazy inode table initialization. +enable lazy inode table zeroing. +.TP +.B lazy_journal_init\fR[\fB= \fI<0 to disable, 1 to enable>\fR] +If enabled, the journal inode will not be fully zeroed out by +.BR mke2fs . +This speeds up filesystem initialization noticeably, but carries some +small risk if the system crashes before the journal has been overwritten +entirely one time. If the option value is omitted, it defaults to 1 to +enable lazy journal inode zeroing. .TP .B test_fs Set a flag in the filesystem superblock indicating that it may be diff --git a/misc/mke2fs.c b/misc/mke2fs.c index b994e1e..9a76ade 100644 --- a/misc/mke2fs.c +++ b/misc/mke2fs.c @@ -382,6 +382,12 @@ static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed) /* The kernel doesn't need to zero the itable blocks */ fs->group_desc[i].bg_flags |= EXT2_BG_INODE_ZEROED; ext2fs_group_desc_csum_set(fs, i); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) + /* zero the designated exclude bitmap block */ + num++; +#endif } retval = ext2fs_zero_blocks(fs, blk, num, &blk, &num); if (retval) { @@ -555,6 +561,10 @@ static void create_journal_dev(ext2_filsys fs) _("while initializing journal superblock")); exit(1); } + + if (journal_flags & EXT2_MKJOURNAL_LAZYINIT) + goto write_superblock; + if (quiet) memset(&progress, 0, sizeof(progress)); else @@ -582,6 +592,8 @@ static void create_journal_dev(ext2_filsys fs) } ext2fs_zero_blocks(0, 0, 0, 0, 0); + progress_close(&progress); +write_superblock: retval = io_channel_write_blk(fs->io, fs->super->s_first_data_block+1, 1, buf); @@ -590,7 +602,6 @@ static void create_journal_dev(ext2_filsys fs) _("while writing journal superblock")); exit(1); } - progress_close(&progress); } static void show_stats(ext2_filsys fs) @@ -806,6 +817,12 @@ static void parse_extended_opts(struct ext2_super_block *param, lazy_itable_init = strtoul(arg, &p, 0); else lazy_itable_init = 1; + } else if (!strcmp(token, "lazy_journal_init")) { + if (arg) + journal_flags |= strtoul(arg, &p, 0) ? + EXT2_MKJOURNAL_LAZYINIT : 0; + else + journal_flags |= EXT2_MKJOURNAL_LAZYINIT; } else if (!strcmp(token, "discard")) { discard = 1; } else if (!strcmp(token, "nodiscard")) { @@ -825,6 +842,7 @@ static void parse_extended_opts(struct ext2_super_block *param, "\tstripe-width=<RAID stride * data disks in blocks>\n" "\tresize=<resize maximum size in blocks>\n" "\tlazy_itable_init=<0 to disable, 1 to enable>\n" + "\tlazy_journal_init=<0 to disable, 1 to enable>\n" "\ttest_fs\n" "\tdiscard\n" "\tnodiscard\n\n"), @@ -844,6 +862,9 @@ static void parse_extended_opts(struct ext2_super_block *param, static __u32 ok_features[3] = { /* Compat */ EXT3_FEATURE_COMPAT_HAS_JOURNAL | +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + EXT2_FEATURE_COMPAT_EXCLUDE_INODE | +#endif EXT2_FEATURE_COMPAT_RESIZE_INODE | EXT2_FEATURE_COMPAT_DIR_INDEX | EXT2_FEATURE_COMPAT_EXT_ATTR, @@ -855,6 +876,9 @@ static __u32 ok_features[3] = { EXT4_FEATURE_INCOMPAT_FLEX_BG, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT| +#endif EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| @@ -990,6 +1014,12 @@ static char **parse_fs_type(const char *fs_type, ext_type = "ext2"; else if (!strcmp(program_name, "mke3fs")) ext_type = "ext3"; +#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL + else if (!strcmp(program_name, "mkfs.next3")) + ext_type = "ext3"; + else if (!strcmp(program_name, "mkfs.next4")) + ext_type = "ext4"; +#endif else if (progname) { ext_type = strrchr(progname, '/'); if (ext_type) @@ -1262,6 +1292,28 @@ static void PRS(int argc, char *argv[]) if (!strcmp(program_name, "mkfs.ext3") || !strcmp(program_name, "mke3fs")) journal_size = -1; +#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL + + /* If called as mkfs.next3/next4/ext4dev: */ + if (!strcmp(program_name, "mkfs.next3") || + !strcmp(program_name, "mkfs.next4") || + !strcmp(program_name, "mkfs.ext4dev")) { + /* 1. create a big journal */ + journal_size = -EXT4_MAX_COW_CREDITS; + /* 2. use system page size as block size */ + blocksize = sys_page_size; + fs_param.s_log_block_size = + int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + /* 3. create exclude inode */ + edit_feature("exclude_inode", &fs_param.s_feature_compat); +#endif +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + /* 4. enable snapshot support */ + edit_feature("has_snapshot", &fs_param.s_feature_compat); +#endif + } +#endif } while ((c = getopt (argc, argv, @@ -1788,6 +1840,9 @@ got_size: "lazy_itable_init", lazy_itable_init); discard = get_bool_from_profile(fs_types, "discard" , discard); + journal_flags |= get_bool_from_profile(fs_types, + "lazy_journal_init", 0) ? + EXT2_MKJOURNAL_LAZYINIT : 0; /* Get options from profile */ for (cpp = fs_types; *cpp; cpp++) { @@ -2282,6 +2337,17 @@ int main (int argc, char *argv[]) exit(1); } } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) { + retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); + if (retval) { + com_err("ext2fs_create_exclude_inode", retval, + _("while reserving blocks for exclude bitmap")); + exit(1); + } + } +#endif } if (journal_device) { diff --git a/misc/tune2fs.c b/misc/tune2fs.c index e5722a5..d756afe 100644 --- a/misc/tune2fs.c +++ b/misc/tune2fs.c @@ -118,6 +118,9 @@ static void usage(void) static __u32 ok_features[3] = { /* Compat */ EXT3_FEATURE_COMPAT_HAS_JOURNAL | +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + EXT2_FEATURE_COMPAT_EXCLUDE_INODE | +#endif EXT2_FEATURE_COMPAT_DIR_INDEX, /* Incompat */ EXT2_FEATURE_INCOMPAT_FILETYPE | @@ -125,6 +128,9 @@ static __u32 ok_features[3] = { EXT4_FEATURE_INCOMPAT_FLEX_BG, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT| +#endif EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| @@ -135,6 +141,9 @@ static __u32 ok_features[3] = { static __u32 clear_ok_features[3] = { /* Compat */ EXT3_FEATURE_COMPAT_HAS_JOURNAL | +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + EXT2_FEATURE_COMPAT_EXCLUDE_INODE | +#endif EXT2_FEATURE_COMPAT_RESIZE_INODE | EXT2_FEATURE_COMPAT_DIR_INDEX, /* Incompat */ @@ -142,6 +151,9 @@ static __u32 clear_ok_features[3] = { EXT4_FEATURE_INCOMPAT_FLEX_BG, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT| +#endif EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| @@ -249,23 +261,170 @@ no_valid_journal: free(journal_path); } +#define blk64_t blk_t +#define ext2fs_block_iterate3 \ + ext2fs_block_iterate2 +#define ext2fs_unmark_block_bitmap2 \ + ext2fs_unmark_block_bitmap +#define ext2fs_group_of_blk2 \ + ext2fs_group_of_blk +#define ext2fs_bg_free_blocks_count_set(fs, group, count) \ + fs->group_desc[group].bg_free_blocks_count = (count) +#define ext2fs_bg_free_blocks_count(fs, group) \ + fs->group_desc[group].bg_free_blocks_count +#define ext2fs_free_blocks_count_add(sb, count) \ + sb->s_free_blocks_count += (count) +#define ext2fs_group_desc(fs, gdp, grp) \ + (gdp)+(grp) + /* Helper function for remove_journal_inode */ -static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, - int blockcnt EXT2FS_ATTR((unused)), +static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr, + e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), void *private EXT2FS_ATTR((unused))) { - blk_t block; + blk64_t block; int group; block = *blocknr; - ext2fs_unmark_block_bitmap(fs->block_map, block); - group = ext2fs_group_of_blk(fs, block); - fs->group_desc[group].bg_free_blocks_count++; + ext2fs_unmark_block_bitmap2(fs->block_map, block); + group = ext2fs_group_of_blk2(fs, block); + ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1); ext2fs_group_desc_csum_set(fs, group); - fs->super->s_free_blocks_count++; + ext2fs_free_blocks_count_add(fs->super, 1); return 0; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +/* + * Remove a special inode from the filesystem: + * - resize inode, @nlink = 0 + * - exclude inode, @nlink = 0 + * - snapshot inodes, @nlink = 1 (snapshots directory) + */ +static void remove_special_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, int nlink) +{ + int retval = ext2fs_read_bitmaps(fs); + if (retval) { + com_err(program_name, retval, + _("while reading bitmaps")); + exit(1); + } + retval = ext2fs_block_iterate3(fs, ino, + BLOCK_FLAG_READ_ONLY, NULL, + release_blocks_proc, NULL); + if (retval) { + com_err(program_name, retval, + _("while clearing inode")); + exit(1); + } + if (nlink) { + /* reset truncated inode */ + inode->i_size = 0; + inode->i_size_high = 0; + inode->i_blocks = 0; + memset(inode->i_block, 0, sizeof(inode->i_block)); + } else { + /* clear unlinked inode */ + memset(inode, 0, sizeof(*inode)); + } + ext2fs_mark_bb_dirty(fs); + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; +} + +#endif +#ifdef EXT2FS_SNAPSHOT_CLEANUP +/* + * Discard snapshots list (free all snapshot blocks) + */ +static void discard_snapshot_list(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + struct ext2_inode inode; + ext2_ino_t ino = sb->s_snapshot_list; + errcode_t retval; + int i = 0; + + if (!ino) + /* no snapshot list, but maybe active snapshot exists? */ + ino = sb->s_snapshot_inum; + if (ino) + fputs(_("Discarding snapshots: "), stderr); + + while (ino) { + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while reading snapshot inode %u"), + ino); + exit(1); + } + + remove_special_inode(fs, ino, &inode, 1); + + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while writing snapshot inode %u"), + ino); + exit(1); + } + + fprintf(stderr, _("%u,"), inode.i_generation); + ino = inode.i_next_snapshot; + i++; + } + + if (i > 0) { + sb->s_snapshot_inum = 0; + sb->s_snapshot_id = 0; + sb->s_snapshot_r_blocks_count = 0; + sb->s_snapshot_list = 0; + fputs(_("done\n"), stderr); + } + + /* no snapshots, so no snapshot problems to fix */ + sb->s_flags &= ~EXT2_FLAGS_FIX_SNAPSHOT; + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + ext2fs_mark_super_dirty(fs); +} + +#endif +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +/* + * Remove the exclude inode from the filesystem + */ +static void remove_exclude_inode(ext2_filsys fs) +{ + struct ext2_inode inode; + ino_t ino = EXT2_EXCLUDE_INO; + errcode_t retval; + + /* clear fix_exclude flag */ + fs->super->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE; + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + ext2fs_mark_super_dirty(fs); + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while reading exclude inode")); + exit(1); + } + + remove_special_inode(fs, ino, &inode, 0); + + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while writing exclude inode")); + exit(1); + } +} + +#endif /* * Remove the journal inode from the filesystem */ @@ -281,6 +440,11 @@ static void remove_journal_inode(ext2_filsys fs) _("while reading journal inode")); exit(1); } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + if (ino == EXT2_JOURNAL_INO) + remove_special_inode(fs, ino, &inode, 0); + else +#else if (ino == EXT2_JOURNAL_INO) { retval = ext2fs_read_bitmaps(fs); if (retval) { @@ -288,9 +452,9 @@ static void remove_journal_inode(ext2_filsys fs) _("while reading bitmaps")); exit(1); } - retval = ext2fs_block_iterate(fs, ino, - BLOCK_FLAG_READ_ONLY, NULL, - release_blocks_proc, NULL); + retval = ext2fs_block_iterate3(fs, ino, + BLOCK_FLAG_READ_ONLY, NULL, + release_blocks_proc, NULL); if (retval) { com_err(program_name, retval, _("while clearing journal inode")); @@ -300,6 +464,7 @@ static void remove_journal_inode(ext2_filsys fs) ext2fs_mark_bb_dirty(fs); fs->flags &= ~EXT2_FLAG_SUPER_ONLY; } else +#endif inode.i_flags &= ~EXT2_IMMUTABLE_FL; retval = ext2fs_write_inode(fs, ino, &inode); if (retval) { @@ -338,6 +503,34 @@ static void request_fsck_afterwards(ext2_filsys fs) printf(_("(and reboot afterwards!)\n")); } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +static int verify_clean_fs(ext2_filsys fs, int compat, unsigned int mask, + int on) +{ + struct ext2_super_block *sb= fs->super; + + if ((mount_flags & EXT2_MF_MOUNTED) && + !(mount_flags & EXT2_MF_READONLY)) { + fprintf(stderr, _("The '%s' feature may only be " + "%s when the filesystem is\n" + "unmounted or mounted read-only.\n"), + e2p_feature2string(compat, mask), + on ? "set" : "cleared"); + exit(1); + } + if (sb->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_RECOVER) { + fprintf(stderr, _("The needs_recovery flag is set. " + "Please run e2fsck before %s\n" + "the '%s' flag.\n"), + on ? "setting" : "clearing", + e2p_feature2string(compat, mask)); + exit(1); + } + return 1; +} + +#endif /* * Update the feature set as provided by the user. */ @@ -356,11 +549,32 @@ static void update_feature_set(ext2_filsys fs, char *features) !((&sb->s_feature_compat)[(type)] & (mask))) #define FEATURE_CHANGED(type, mask) ((mask) & \ (old_features[(type)] ^ (&sb->s_feature_compat)[(type)])) +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +#define FEATURE_ON_SAFE(compat, mask) \ + (FEATURE_ON(compat, mask) && verify_clean_fs(fs, compat, mask, 1)) +#define FEATURE_OFF_SAFE(compat, mask) \ + (FEATURE_OFF(compat, mask) && verify_clean_fs(fs, compat, mask, 0)) +#endif old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat; old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat; old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat; +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + /* disallow changing features when filesystem has snapshots */ + if (sb->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) { + ok_features[E2P_FEATURE_COMPAT] = 0; + ok_features[E2P_FEATURE_INCOMPAT] = 0; + ok_features[E2P_FEATURE_RO_INCOMPAT] = + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT; + clear_ok_features[E2P_FEATURE_COMPAT] = 0; + clear_ok_features[E2P_FEATURE_INCOMPAT] = 0; + clear_ok_features[E2P_FEATURE_RO_INCOMPAT] = + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT; + } + +#endif if (e2p_edit_feature2(features, &sb->s_feature_compat, ok_features, clear_ok_features, &type_err, &mask_err)) { @@ -368,6 +582,14 @@ static void update_feature_set(ext2_filsys fs, char *features) fprintf(stderr, _("Invalid filesystem option set: %s\n"), features); +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + else if (old_features[E2P_FEATURE_RO_INCOMPAT] & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) + fputs(_("The filesystem has snapshots. " + "Please clear the has_snapshot flag\n" + "before clearing/setting other filesystem flags.\n"), + stderr); +#endif else if (type_err & E2P_FEATURE_NEGATE_FLAG) fprintf(stderr, _("Clearing filesystem feature '%s' " "not supported.\n"), @@ -416,6 +638,78 @@ static void update_feature_set(ext2_filsys fs, char *features) sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + if (FEATURE_OFF_SAFE(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) { + remove_exclude_inode(fs); + /* clear exclude_bitmap along with exclude_inode */ + sb->s_feature_compat &= + ~EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP; + } + + if (FEATURE_ON_SAFE(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) { + retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); + if (retval) { + com_err(program_name, retval, + _("while creating exclude inode")); + exit(1); + } + } + +#endif +#ifdef EXT2FS_SNAPSHOT_CLEANUP + if (FEATURE_OFF_SAFE(E2P_FEATURE_RO_INCOMPAT, + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) { + discard_snapshot_list(fs); + if (sb->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) { + /* reset exclude bitmap blocks */ + retval = ext2fs_create_exclude_inode(fs, EXCLUDE_RESET); + if (retval) + /* cleanup bad exclude inode/bitmap */ + sb->s_feature_compat &= + ~(EXT2_FEATURE_COMPAT_EXCLUDE_INODE| + EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP); + } + } + +#endif +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + if (FEATURE_ON_SAFE(E2P_FEATURE_RO_INCOMPAT, + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) { + int big_journal = 0; + + if ((sb->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { + /* update 'big_journal' flag */ + big_journal = (ext2fs_check_journal_size(fs) >= + EXT4_BIG_JOURNAL_BLOCKS); + } else if (!journal_size || journal_size == -1) { + /* Create a big journal for snapshots */ + journal_size = -EXT4_MAX_COW_CREDITS; + big_journal = 1; + } + + if (!big_journal) + fprintf(stderr, + _("Warning: journal size is not big enough.\n" + "For best operation of Next3, try re-creating " + "the journal with '-J big' before setting the " + "'has_snapshot' flag.\n")); + + /* allocate/reset exclude bitmap blocks */ + retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); + if (!retval) + sb->s_feature_compat |= + EXT2_FEATURE_COMPAT_EXCLUDE_INODE; + else + fprintf(stderr, + _("Warning: failed to create exclude inode.\n" + "For best operation of Next3, try re-creating " + "the exclude inode before setting the " + "'has_snapshot' flag.\n")); + } + +#endif if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) { if (!sb->s_def_hash_version) sb->s_def_hash_version = EXT2_HASH_HALF_MD4; diff --git a/misc/util.c b/misc/util.c index 2897937..a37767b 100644 --- a/misc/util.c +++ b/misc/util.c @@ -222,6 +222,22 @@ void parse_journal_opts(const char *opts) journal_size = strtoul(arg, &p, 0); if (*p) journal_usage++; +#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL + } else if (strcmp(token, "big") == 0) { + /* Create a big journal for snapshots */ + journal_size = -EXT4_MAX_COW_CREDITS; + continue; + } else if (strcmp(token, "bigger") == 0) { + /* Create a journal bigger than default */ + if (!arg) { + journal_usage++; + continue; + } + journal_size = -strtoul(arg, &p, 0); + if (*p) + journal_usage++; + continue; +#endif } else if (strcmp(token, "v1_superblock") == 0) { journal_flags |= EXT2_MKJOURNAL_V1_SUPER; continue; @@ -235,6 +251,10 @@ void parse_journal_opts(const char *opts) "\tis set off by an equals ('=') sign.\n\n" "Valid journal options are:\n" "\tsize=<journal size in megabytes>\n" +#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL + "\tbig (Next3 big journal size)\n" + "\tbigger=<X times bigger than default size>\n" +#endif "\tdevice=<journal device>\n\n" "The journal size must be between " "1024 and 10240000 filesystem blocks.\n\n"), stderr); @@ -263,6 +283,22 @@ unsigned int figure_journal_size(int size, ext2_filsys fs) return 0; } +#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL + if (size < -1) { + /* bigger journal requested */ + j_blocks = ext2fs_big_journal_size(-size, fs->super->s_blocks_count); + if (j_blocks < EXT3_DEF_JOURNAL_BLOCKS*(-size)) { + fputs(_("\nFilesystem too small for requested " + "journal size. "), stderr); + if (j_blocks < 0) { + fputs(_("Aborting.\n"), stderr); + exit(1); + } + fputs(_("Creating a smaller journal.\n"), stderr); + } + } + +#endif if (size > 0) { j_blocks = size * 1024 / (fs->blocksize / 1024); if (j_blocks < 1024 || j_blocks > 10240000) { diff --git a/resize/main.c b/resize/main.c index bb04d0b..f386ae3 100644 --- a/resize/main.c +++ b/resize/main.c @@ -452,6 +452,19 @@ int main (int argc, char ** argv) if (mount_flags & EXT2_MF_MOUNTED) { retval = online_resize_fs(fs, mtpt, &new_size, flags); } else { +#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT + /* do not offline resize a volume with active snapshot */ + if (!force && (fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) && + fs->super->s_snapshot_inum) { + fprintf(stderr, + _("offline resize will damage next3 snapshots " + "on %s - Please mount the filesystem " + "for online resize.\n\n"), + device_name); + exit(1); + } +#endif if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) || (fs->super->s_state & EXT2_ERROR_FS) || ((fs->super->s_state & EXT2_VALID_FS) == 0))) { diff --git a/resize/resize2fs.c b/resize/resize2fs.c index 024f1cc..333d656 100644 --- a/resize/resize2fs.c +++ b/resize/resize2fs.c @@ -48,6 +48,9 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs); static errcode_t inode_ref_fix(ext2_resize_t rfs); static errcode_t move_itables(ext2_resize_t rfs); static errcode_t fix_resize_inode(ext2_filsys fs); +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +static errcode_t fix_exclude_inode(ext2_filsys fs); +#endif static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs); static errcode_t fix_sb_journal_backup(ext2_filsys fs); @@ -153,6 +156,12 @@ errcode_t resize_fs(ext2_filsys fs, blk_t *new_size, int flags, if (retval) goto errout; +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE + retval = fix_exclude_inode(rfs->new_fs); + if (retval) + goto errout; + +#endif retval = fix_sb_journal_backup(rfs->new_fs); if (retval) goto errout; @@ -1754,6 +1763,27 @@ errout: return retval; } +#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE +/* + * Fix the exclude inode + */ +static errcode_t fix_exclude_inode(ext2_filsys fs) +{ + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) + return 0; + /* + * create_exclude_inode(): + * - updates exclude_blks for existing block groups + * - allocates exclude bitmap blocks for new block groups + * - doesn't free exclude bitmap blocks of deleted block group, + * so when resizing from large to small filesystem, + * it would be wise to remove the exclude inode beforehand. + */ + return ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); +} + +#endif /* * Finally, recalculate the summary information */ diff --git a/version.h b/version.h index 7005be1..5d50d94 100644 --- a/version.h +++ b/version.h @@ -7,5 +7,5 @@ * file may be redistributed under the GNU Public License v2. */ -#define E2FSPROGS_VERSION "1.41.14" -#define E2FSPROGS_DATE "22-Dec-2010" +#define E2FSPROGS_VERSION "1.41.14-next4-1.0.13-7" +#define E2FSPROGS_DATE "23-Jun-2011"