From 87ad00d142b762e74d0bb65f73bfd84409cf283c Mon Sep 17 00:00:00 2001 From: "cantab.net!aia21" Date: Fri, 29 Nov 2002 12:16:35 +0000 Subject: [PATCH] mvdir }(Logical change 1.23) --- ntfstools/Makefile.am | 77 - ntfstools/attrdef.c | 153 -- ntfstools/boot.c | 218 --- ntfstools/dumplog.c | 310 --- ntfstools/mkntfs.8.in | 213 -- ntfstools/mkntfs.c | 3554 ---------------------------------- ntfstools/ntfsdump_logfile.c | 371 ---- ntfstools/ntfsfix.8.in | 56 - ntfstools/ntfsfix.c | 317 --- ntfstools/ntfsinfo.8.in | 25 - ntfstools/ntfsinfo.c | 195 -- ntfstools/ntfslabel.8.in | 54 - ntfstools/ntfslabel.c | 247 --- ntfstools/ntfsresize.8.in | 117 -- ntfstools/ntfsresize.c | 914 --------- ntfstools/ntfsundelete.8.in | 344 ---- ntfstools/ntfsundelete.c | 2018 ------------------- ntfstools/ntfsundelete.h | 104 - ntfstools/ntfswipe.c | 732 ------- ntfstools/ntfswipe.h | 47 - ntfstools/sd.c | 200 -- ntfstools/upcase.c | 82 - 22 files changed, 10348 deletions(-) delete mode 100644 ntfstools/Makefile.am delete mode 100644 ntfstools/attrdef.c delete mode 100644 ntfstools/boot.c delete mode 100644 ntfstools/dumplog.c delete mode 100644 ntfstools/mkntfs.8.in delete mode 100644 ntfstools/mkntfs.c delete mode 100644 ntfstools/ntfsdump_logfile.c delete mode 100644 ntfstools/ntfsfix.8.in delete mode 100644 ntfstools/ntfsfix.c delete mode 100644 ntfstools/ntfsinfo.8.in delete mode 100644 ntfstools/ntfsinfo.c delete mode 100644 ntfstools/ntfslabel.8.in delete mode 100644 ntfstools/ntfslabel.c delete mode 100644 ntfstools/ntfsresize.8.in delete mode 100644 ntfstools/ntfsresize.c delete mode 100644 ntfstools/ntfsundelete.8.in delete mode 100644 ntfstools/ntfsundelete.c delete mode 100644 ntfstools/ntfsundelete.h delete mode 100644 ntfstools/ntfswipe.c delete mode 100644 ntfstools/ntfswipe.h delete mode 100644 ntfstools/sd.c delete mode 100644 ntfstools/upcase.c diff --git a/ntfstools/Makefile.am b/ntfstools/Makefile.am deleted file mode 100644 index 81600372..00000000 --- a/ntfstools/Makefile.am +++ /dev/null @@ -1,77 +0,0 @@ -# Need this to enable 64-bit (device) file access functions and parameters. -if DEBUG -AM_CFLAGS = -D_FILE_OFFSET_BITS=64 -g -DDEBUG -Wall -else -AM_CFLAGS = -D_FILE_OFFSET_BITS=64 -endif - -if REALLYSTATIC -AM_LIBS = $(top_srcdir)/libntfs/.libs/libntfs.a -AM_LFLAGS = -static -STATIC_LINK = $(CC) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ -else -AM_LIBS = $(top_srcdir)/libntfs/libntfs.la -AM_LFLAGS = $(all_libraries) -LIBTOOL_LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ -endif - -# Workaround to make REALLYSTATIC work with automake 1.5. -LINK=$(STATIC_LINK) $(LIBTOOL_LINK) - -bin_PROGRAMS = ntfsfix ntfsinfo -sbin_PROGRAMS = mkntfs ntfslabel ntfsundelete ntfsresize -EXTRA_PROGRAMS = ntfsdump_logfile dumplog ntfswipe - -man_MANS = mkntfs.8 ntfsfix.8 ntfslabel.8 ntfsinfo.8 ntfsundelete.8 \ - ntfsresize.8 -EXTRA_MANS = - -CLEANFILES = $(EXTRA_PROGRAMS) - -linux_ntfsincludedir = -I$(top_srcdir)/include - -# Set the include path. -INCLUDES = -I$(top_srcdir)/include $(all_includes) - -ntfsfix_SOURCES = ntfsfix.c -ntfsfix_LDADD = $(AM_LIBS) -ntfsfix_LDFLAGS = $(AM_LFLAGS) - -mkntfs_SOURCES = attrdef.c upcase.c boot.c sd.c mkntfs.c -mkntfs_LDADD = $(AM_LIBS) -mkntfs_LDFLAGS = $(AM_LFLAGS) - -ntfslabel_SOURCES = ntfslabel.c -ntfslabel_LDADD = $(AM_LIBS) -ntfslabel_LDFLAGS = $(AM_LFLAGS) - -ntfsinfo_SOURCES = ntfsinfo.c -ntfsinfo_LDADD = $(AM_LIBS) -ntfsinfo_LDFLAGS = $(AM_LFLAGS) - -ntfsundelete_SOURCES = ntfsundelete.c ntfsundelete.h -ntfsundelete_LDADD = $(AM_LIBS) -ntfsundelete_LDFLAGS = $(AM_LFLAGS) - -ntfsresize_SOURCES = ntfsresize.c -ntfsresize_LDADD = $(AM_LIBS) -ntfsresize_LDFLAGS = $(AM_LFLAGS) - -# We don't distribute these - -ntfswipe_SOURCES = ntfswipe.c ntfswipe.h -ntfswipe_LDADD = $(AM_LIBS) -ntfswipe_LDFLAGS = $(AM_LFLAGS) - -ntfsdump_logfile_SOURCES= ntfsdump_logfile.c -ntfsdump_logfile_LDADD = $(AM_LIBS) -ntfsdump_logfile_LDFLAGS= $(AM_LFLAGS) - -dumplog_SOURCES = dumplog.c -dumplog_LDADD = $(AM_LIBS) -dumplog_LDFLAGS = $(AM_LFLAGS) - -# Extra targets - -strip: $(bin_PROGRAMS) $(sbin_PROGRAMS) - $(STRIP) $^ diff --git a/ntfstools/attrdef.c b/ntfstools/attrdef.c deleted file mode 100644 index c9bb82c6..00000000 --- a/ntfstools/attrdef.c +++ /dev/null @@ -1,153 +0,0 @@ -const unsigned char attrdef_ntfs12_array[2400] = { - 36, 0, 83, 0, 84, 0, 65, 0, 78, 0, 68, 0, 65, 0, 82, 0, - 68, 0, 95, 0, 73, 0, 78, 0, 70, 0, 79, 0, 82, 0, 77, 0, - 65, 0, 84, 0, 73, 0, 79, 0, 78, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 48, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, - 36, 0, 65, 0, 84, 0, 84, 0, 82, 0, 73, 0, 66, 0, 85, 0, - 84, 0, 69, 0, 95, 0, 76, 0, 73, 0, 83, 0, 84, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 70, 0, 73, 0, 76, 0, 69, 0, 95, 0, 78, 0, 65, 0, - 77, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, - 68, 0, 0, 0, 0, 0, 0, 0, 66, 2, 0, 0, 0, 0, 0, 0, - 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, - 86, 0, 69, 0, 82, 0, 83, 0, 73, 0, 79, 0, 78, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, - 36, 0, 83, 0, 69, 0, 67, 0, 85, 0, 82, 0, 73, 0, 84, 0, - 89, 0, 95, 0, 68, 0, 69, 0, 83, 0, 67, 0, 82, 0, 73, 0, - 80, 0, 84, 0, 79, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, - 78, 0, 65, 0, 77, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, - 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, - 73, 0, 78, 0, 70, 0, 79, 0, 82, 0, 77, 0, 65, 0, 84, 0, - 73, 0, 79, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 12, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, - 36, 0, 68, 0, 65, 0, 84, 0, 65, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 73, 0, 78, 0, 68, 0, 69, 0, 88, 0, 95, 0, 82, 0, - 79, 0, 79, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 73, 0, 78, 0, 68, 0, 69, 0, 88, 0, 95, 0, 65, 0, - 76, 0, 76, 0, 79, 0, 67, 0, 65, 0, 84, 0, 73, 0, 79, 0, - 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 66, 0, 73, 0, 84, 0, 77, 0, 65, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 83, 0, 89, 0, 77, 0, 66, 0, 79, 0, 76, 0, 73, 0, - 67, 0, 95, 0, 76, 0, 73, 0, 78, 0, 75, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 69, 0, 65, 0, 95, 0, 73, 0, 78, 0, 70, 0, 79, 0, - 82, 0, 77, 0, 65, 0, 84, 0, 73, 0, 79, 0, 78, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, - 36, 0, 69, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - diff --git a/ntfstools/boot.c b/ntfstools/boot.c deleted file mode 100644 index b47ded9c..00000000 --- a/ntfstools/boot.c +++ /dev/null @@ -1,218 +0,0 @@ -/* The first 3429 bytes of $Boot. The rest is just zero. Total 8192 bytes. */ -const unsigned char boot_array[3429] = { -235, 91, 144, 78, 84, 70, 83, 32, 32, 32, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 51, 192, -142, 208, 188, 0, 124, 251, 184, 192, 7, 142, 216, 199, 6, 84, 0, 0, - 0, 199, 6, 86, 0, 0, 0, 199, 6, 91, 0, 16, 0, 184, 0, 13, -142, 192, 43, 219, 232, 7, 0, 104, 0, 13, 104, 102, 2, 203, 80, 83, - 81, 82, 6, 102, 161, 84, 0, 102, 3, 6, 28, 0, 102, 51, 210, 102, - 15, 183, 14, 24, 0, 102, 247, 241, 254, 194, 136, 22, 90, 0, 102, 139, -208, 102, 193, 234, 16, 247, 54, 26, 0, 136, 22, 37, 0, 163, 88, 0, -161, 24, 0, 42, 6, 90, 0, 64, 59, 6, 91, 0, 118, 3, 161, 91, - 0, 80, 180, 2, 139, 22, 88, 0, 177, 6, 210, 230, 10, 54, 90, 0, -139, 202, 134, 233, 138, 54, 37, 0, 178, 128, 205, 19, 88, 114, 42, 1, - 6, 84, 0, 131, 22, 86, 0, 0, 41, 6, 91, 0, 118, 11, 193, 224, - 5, 140, 194, 3, 208, 142, 194, 235, 138, 7, 90, 89, 91, 88, 195, 190, - 89, 1, 235, 8, 190, 227, 1, 235, 3, 190, 57, 1, 232, 9, 0, 190, -173, 1, 232, 3, 0, 251, 235, 254, 172, 60, 0, 116, 9, 180, 14, 187, - 7, 0, 205, 16, 235, 242, 195, 29, 0, 65, 32, 100, 105, 115, 107, 32, -114, 101, 97, 100, 32, 101, 114, 114, 111, 114, 32, 111, 99, 99, 117, 114, -114, 101, 100, 46, 13, 10, 0, 41, 0, 65, 32, 107, 101, 114, 110, 101, -108, 32, 102, 105, 108, 101, 32, 105, 115, 32, 109, 105, 115, 115, 105, 110, -103, 32, 102, 114, 111, 109, 32, 116, 104, 101, 32, 100, 105, 115, 107, 46, - 13, 10, 0, 37, 0, 65, 32, 107, 101, 114, 110, 101, 108, 32, 102, 105, -108, 101, 32, 105, 115, 32, 116, 111, 111, 32, 100, 105, 115, 99, 111, 110, -116, 105, 103, 117, 111, 117, 115, 46, 13, 10, 0, 51, 0, 73, 110, 115, -101, 114, 116, 32, 97, 32, 115, 121, 115, 116, 101, 109, 32, 100, 105, 115, -107, 101, 116, 116, 101, 32, 97, 110, 100, 32, 114, 101, 115, 116, 97, 114, -116, 13, 10, 116, 104, 101, 32, 115, 121, 115, 116, 101, 109, 46, 13, 10, - 0, 23, 0, 92, 78, 84, 76, 68, 82, 32, 105, 115, 32, 99, 111, 109, -112, 114, 101, 115, 115, 101, 100, 46, 13, 10, 0, 0, 0, 0, 85, 170, - 5, 0, 78, 0, 84, 0, 76, 0, 68, 0, 82, 0, 4, 0, 36, 0, - 73, 0, 51, 0, 48, 0, 0, 224, 0, 0, 0, 48, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 140, 200, 142, 216, 193, 224, 4, 250, 139, 224, -251, 102, 15, 183, 6, 11, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, -102, 163, 78, 2, 102, 139, 14, 64, 0, 128, 249, 0, 15, 143, 14, 0, -246, 217, 102, 184, 1, 0, 0, 0, 102, 211, 224, 235, 8, 144, 102, 161, - 78, 2, 102, 247, 225, 102, 163, 82, 2, 102, 15, 183, 30, 11, 0, 102, - 51, 210, 102, 247, 243, 102, 163, 86, 2, 232, 44, 4, 102, 139, 14, 74, - 2, 102, 137, 14, 34, 2, 102, 3, 14, 82, 2, 102, 137, 14, 38, 2, -102, 3, 14, 82, 2, 102, 137, 14, 42, 2, 102, 3, 14, 82, 2, 102, -137, 14, 58, 2, 102, 3, 14, 82, 2, 102, 137, 14, 66, 2, 102, 184, -144, 0, 0, 0, 102, 139, 14, 34, 2, 232, 65, 9, 102, 11, 192, 15, -132, 22, 254, 102, 163, 46, 2, 102, 184, 160, 0, 0, 0, 102, 139, 14, - 38, 2, 232, 40, 9, 102, 163, 50, 2, 102, 184, 176, 0, 0, 0, 102, -139, 14, 42, 2, 232, 22, 9, 102, 163, 54, 2, 102, 161, 46, 2, 102, - 11, 192, 15, 132, 227, 253, 103, 128, 120, 8, 0, 15, 133, 218, 253, 103, -102, 141, 80, 16, 103, 3, 66, 4, 103, 102, 15, 182, 72, 12, 102, 137, - 14, 94, 2, 103, 102, 139, 72, 8, 102, 137, 14, 90, 2, 102, 161, 90, - 2, 102, 15, 183, 14, 11, 0, 102, 51, 210, 102, 247, 241, 102, 163, 98, - 2, 102, 161, 66, 2, 102, 3, 6, 90, 2, 102, 163, 70, 2, 102, 131, - 62, 50, 2, 0, 15, 132, 25, 0, 102, 131, 62, 54, 2, 0, 15, 132, -135, 253, 102, 139, 30, 54, 2, 30, 7, 102, 139, 62, 70, 2, 232, 177, - 1, 102, 15, 183, 14, 0, 2, 102, 184, 2, 2, 0, 0, 232, 153, 6, -102, 11, 192, 15, 132, 88, 253, 103, 102, 139, 0, 30, 7, 102, 139, 62, - 58, 2, 232, 209, 4, 102, 161, 58, 2, 102, 187, 128, 0, 0, 0, 102, -185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 203, 0, 102, 11, -192, 15, 132, 42, 253, 103, 102, 15, 183, 88, 12, 102, 129, 227, 255, 0, - 0, 0, 15, 133, 30, 253, 102, 139, 216, 104, 0, 32, 7, 102, 43, 255, -232, 79, 1, 138, 22, 36, 0, 184, 232, 3, 142, 192, 141, 54, 11, 0, - 43, 192, 104, 0, 32, 80, 203, 80, 83, 81, 82, 6, 255, 54, 91, 0, -255, 54, 84, 0, 255, 54, 86, 0, 139, 195, 193, 232, 4, 140, 193, 3, -193, 37, 255, 15, 45, 0, 16, 247, 216, 139, 14, 91, 0, 193, 225, 5, - 81, 59, 193, 118, 2, 139, 193, 80, 193, 232, 5, 163, 91, 0, 232, 61, -252, 88, 89, 43, 200, 118, 11, 140, 194, 3, 208, 142, 194, 184, 0, 16, -235, 222, 143, 6, 86, 0, 143, 6, 84, 0, 143, 6, 91, 0, 7, 90, - 89, 91, 88, 195, 6, 30, 102, 96, 102, 139, 218, 102, 15, 182, 14, 13, - 0, 102, 247, 225, 102, 163, 84, 0, 102, 139, 195, 102, 247, 225, 163, 91, - 0, 139, 223, 131, 227, 15, 140, 192, 102, 193, 239, 4, 3, 199, 80, 7, -232, 116, 255, 102, 97, 144, 31, 7, 195, 103, 3, 64, 20, 103, 102, 131, - 56, 255, 15, 132, 76, 0, 103, 102, 57, 24, 15, 133, 51, 0, 102, 11, -201, 15, 133, 10, 0, 103, 128, 120, 9, 0, 15, 133, 35, 0, 195, 103, - 58, 72, 9, 15, 133, 26, 0, 102, 139, 240, 103, 3, 112, 10, 232, 61, - 5, 102, 81, 30, 7, 102, 139, 250, 243, 167, 102, 89, 15, 133, 1, 0, -195, 103, 102, 131, 120, 4, 0, 15, 132, 7, 0, 103, 102, 3, 64, 4, -235, 171, 102, 43, 192, 195, 102, 139, 243, 232, 18, 5, 103, 102, 3, 0, -103, 247, 64, 12, 2, 0, 15, 133, 52, 0, 103, 102, 141, 80, 16, 103, - 58, 74, 64, 15, 133, 24, 0, 103, 102, 141, 114, 66, 232, 239, 4, 102, - 81, 30, 7, 102, 139, 251, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, -131, 120, 8, 0, 15, 132, 6, 0, 103, 3, 64, 8, 235, 194, 102, 51, -192, 195, 103, 128, 123, 8, 0, 15, 133, 28, 0, 6, 30, 102, 96, 103, -102, 141, 83, 16, 103, 102, 139, 10, 102, 139, 243, 103, 3, 114, 4, 243, -164, 102, 97, 144, 31, 7, 195, 103, 102, 141, 83, 16, 103, 102, 139, 74, - 8, 102, 65, 102, 43, 192, 232, 1, 0, 195, 6, 30, 102, 96, 103, 128, -123, 8, 1, 15, 132, 3, 0, 233, 127, 251, 102, 131, 249, 0, 15, 133, - 6, 0, 102, 97, 144, 31, 7, 195, 102, 83, 102, 80, 102, 81, 102, 87, - 6, 232, 87, 3, 102, 139, 209, 7, 102, 95, 102, 89, 102, 59, 202, 15, -141, 3, 0, 102, 139, 209, 232, 171, 254, 102, 43, 202, 102, 139, 218, 102, -139, 194, 102, 15, 182, 22, 13, 0, 102, 247, 226, 102, 15, 183, 22, 11, - 0, 102, 247, 226, 102, 3, 248, 102, 88, 102, 3, 195, 102, 91, 235, 170, - 6, 30, 102, 96, 103, 128, 123, 8, 1, 15, 132, 3, 0, 233, 25, 251, -102, 131, 249, 0, 15, 133, 6, 0, 102, 97, 144, 31, 7, 195, 102, 83, -102, 80, 102, 81, 102, 87, 6, 102, 81, 102, 51, 210, 102, 15, 182, 14, - 13, 0, 102, 247, 241, 102, 82, 232, 225, 2, 102, 15, 182, 30, 13, 0, -102, 247, 227, 102, 90, 102, 3, 194, 102, 80, 102, 15, 182, 6, 13, 0, -102, 247, 225, 102, 139, 208, 102, 88, 102, 89, 7, 102, 95, 102, 89, 102, - 59, 202, 15, 141, 3, 0, 102, 139, 209, 102, 163, 84, 0, 137, 22, 91, - 0, 6, 30, 102, 96, 139, 223, 131, 227, 15, 140, 192, 102, 193, 239, 4, - 3, 199, 80, 7, 232, 160, 253, 102, 97, 144, 31, 7, 102, 43, 202, 102, -139, 218, 102, 139, 194, 102, 15, 183, 22, 11, 0, 102, 247, 226, 102, 3, -248, 102, 88, 102, 3, 195, 102, 91, 233, 101, 255, 6, 30, 102, 96, 38, -103, 102, 15, 183, 95, 4, 38, 103, 102, 15, 183, 79, 6, 102, 11, 201, - 15, 132, 101, 250, 102, 3, 223, 102, 131, 195, 2, 102, 129, 199, 254, 1, - 0, 0, 102, 73, 102, 11, 201, 15, 132, 23, 0, 38, 103, 139, 3, 38, -103, 137, 7, 102, 131, 195, 2, 102, 129, 199, 0, 2, 0, 0, 102, 73, -235, 226, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 184, 1, 0, - 0, 0, 102, 163, 30, 2, 102, 161, 26, 2, 102, 3, 6, 82, 2, 102, -163, 74, 2, 102, 161, 48, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, -102, 163, 84, 0, 102, 161, 86, 2, 163, 91, 0, 102, 139, 30, 26, 2, - 30, 7, 232, 242, 252, 102, 15, 183, 251, 232, 111, 255, 102, 161, 26, 2, -102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, - 0, 0, 232, 100, 253, 102, 11, 192, 15, 132, 87, 0, 102, 139, 216, 30, - 7, 102, 139, 62, 22, 2, 232, 249, 253, 102, 139, 30, 22, 2, 103, 102, -129, 59, 128, 0, 0, 0, 15, 132, 6, 0, 103, 3, 91, 4, 235, 238, -103, 102, 129, 59, 128, 0, 0, 0, 15, 133, 39, 0, 102, 83, 103, 102, -139, 67, 16, 102, 139, 62, 74, 2, 30, 7, 232, 9, 1, 102, 91, 102, -161, 82, 2, 102, 1, 6, 74, 2, 102, 255, 6, 30, 2, 103, 3, 91, - 4, 235, 205, 102, 97, 144, 31, 7, 195, 102, 139, 208, 102, 139, 14, 30, - 2, 102, 161, 26, 2, 102, 82, 102, 80, 102, 81, 102, 82, 102, 187, 128, - 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, -215, 252, 102, 11, 192, 15, 132, 64, 249, 102, 139, 216, 102, 88, 232, 42, - 1, 102, 11, 192, 15, 132, 7, 0, 102, 91, 102, 91, 102, 91, 195, 102, - 89, 102, 88, 102, 90, 102, 3, 6, 82, 2, 226, 185, 102, 51, 192, 195, - 6, 30, 102, 96, 102, 80, 102, 81, 102, 51, 210, 102, 15, 182, 30, 13, - 0, 102, 247, 243, 102, 82, 232, 144, 255, 102, 11, 192, 15, 132, 249, 248, -102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, 163, - 84, 0, 102, 89, 102, 15, 182, 30, 13, 0, 102, 59, 203, 15, 142, 19, - 0, 137, 30, 91, 0, 102, 43, 203, 102, 88, 102, 3, 195, 102, 80, 102, - 81, 235, 20, 144, 102, 88, 102, 3, 193, 102, 80, 137, 14, 91, 0, 102, -185, 0, 0, 0, 0, 102, 81, 6, 102, 87, 139, 223, 131, 227, 15, 140, -192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 155, 251, 102, 95, 7, 102, - 3, 62, 78, 2, 102, 89, 102, 88, 102, 131, 249, 0, 15, 143, 116, 255, -102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 247, 38, 86, 2, 102, -139, 14, 86, 2, 232, 89, 255, 232, 241, 253, 102, 97, 144, 31, 7, 195, - 6, 30, 102, 96, 102, 247, 38, 98, 2, 102, 139, 30, 50, 2, 102, 139, - 14, 98, 2, 30, 7, 102, 139, 62, 66, 2, 232, 35, 253, 232, 203, 253, -102, 97, 144, 31, 7, 195, 102, 80, 102, 83, 102, 81, 102, 139, 30, 70, - 2, 102, 139, 200, 102, 193, 232, 3, 102, 131, 225, 7, 102, 3, 216, 102, -184, 1, 0, 0, 0, 102, 211, 224, 103, 132, 3, 15, 132, 4, 0, 248, -235, 2, 144, 249, 102, 89, 102, 91, 102, 88, 195, 103, 128, 123, 8, 1, - 15, 132, 4, 0, 102, 43, 192, 195, 103, 102, 141, 115, 16, 103, 102, 139, - 86, 8, 102, 59, 194, 15, 135, 11, 0, 103, 102, 139, 22, 102, 59, 194, - 15, 131, 4, 0, 102, 43, 192, 195, 103, 3, 94, 16, 102, 43, 246, 103, -128, 59, 0, 15, 132, 62, 0, 232, 129, 0, 102, 3, 241, 232, 57, 0, -102, 3, 202, 102, 59, 193, 15, 140, 33, 0, 102, 139, 209, 102, 80, 103, -102, 15, 182, 11, 102, 139, 193, 102, 131, 224, 15, 102, 193, 233, 4, 102, - 3, 217, 102, 3, 216, 102, 67, 102, 88, 235, 196, 102, 43, 200, 102, 43, -194, 102, 3, 198, 195, 102, 43, 192, 195, 102, 43, 201, 103, 138, 11, 128, -225, 15, 102, 131, 249, 0, 15, 133, 4, 0, 102, 43, 201, 195, 102, 83, -102, 82, 102, 3, 217, 103, 102, 15, 190, 19, 102, 73, 102, 75, 102, 131, -249, 0, 15, 132, 13, 0, 102, 193, 226, 8, 103, 138, 19, 102, 75, 102, - 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, 195, 102, 83, 102, 82, 102, - 43, 210, 103, 138, 19, 102, 131, 226, 15, 102, 43, 201, 103, 138, 11, 192, -233, 4, 102, 131, 249, 0, 15, 133, 8, 0, 102, 43, 201, 102, 90, 102, - 91, 195, 102, 3, 218, 102, 3, 217, 103, 102, 15, 190, 19, 102, 73, 102, - 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, 193, 226, 8, 103, 138, 19, -102, 75, 102, 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, 195, 102, 11, -201, 15, 133, 1, 0, 195, 102, 81, 102, 86, 103, 131, 62, 97, 15, 140, - 12, 0, 103, 131, 62, 122, 15, 143, 4, 0, 103, 131, 46, 32, 102, 131, -198, 2, 226, 230, 102, 94, 102, 89, 195, 102, 80, 102, 81, 102, 139, 208, -102, 161, 46, 2, 103, 102, 141, 88, 16, 103, 3, 67, 4, 103, 102, 141, - 64, 16, 102, 139, 218, 232, 158, 250, 102, 11, 192, 15, 132, 5, 0, 102, - 89, 102, 89, 195, 102, 161, 50, 2, 102, 11, 192, 15, 133, 8, 0, 102, - 89, 102, 89, 102, 51, 192, 195, 102, 139, 22, 50, 2, 103, 102, 141, 82, - 16, 103, 102, 139, 66, 8, 102, 64, 102, 139, 30, 78, 2, 102, 247, 227, -102, 51, 210, 102, 247, 54, 90, 2, 102, 80, 102, 88, 102, 11, 192, 15, -132, 48, 0, 102, 72, 102, 80, 232, 28, 254, 114, 238, 232, 241, 253, 102, - 90, 102, 89, 102, 91, 102, 83, 102, 81, 102, 82, 102, 161, 66, 2, 103, -102, 141, 64, 24, 232, 47, 250, 102, 11, 192, 116, 206, 102, 89, 102, 89, -102, 89, 195, 102, 89, 102, 89, 102, 51, 192, 195, 6, 30, 102, 96, 102, -139, 54, 66, 2, 102, 185, 32, 0, 0, 0, 102, 247, 193, 3, 0, 0, - 0, 15, 133, 3, 0, 232, 13, 0, 102, 173, 232, 105, 0, 226, 235, 102, - 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 51, 192, 102, 51, 219, 176, - 13, 180, 14, 187, 7, 0, 205, 16, 176, 10, 180, 14, 187, 7, 0, 205, - 16, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 11, 201, 15, 133, - 9, 0, 232, 208, 255, 102, 97, 144, 31, 7, 195, 102, 51, 192, 102, 51, -219, 173, 180, 14, 187, 7, 0, 205, 16, 226, 240, 232, 183, 255, 102, 97, -144, 31, 7, 195, 96, 172, 60, 0, 116, 9, 180, 14, 187, 7, 0, 205, - 16, 235, 242, 97, 144, 195, 6, 30, 102, 96, 102, 185, 8, 0, 0, 0, -102, 139, 208, 102, 131, 226, 15, 102, 82, 102, 193, 232, 4, 226, 241, 102, -185, 8, 0, 0, 0, 102, 88, 102, 131, 248, 9, 15, 143, 7, 0, 102, -131, 192, 48, 235, 9, 144, 102, 131, 232, 10, 102, 131, 192, 65, 102, 51, -219, 180, 14, 187, 7, 0, 205, 16, 226, 219, 176, 32, 180, 14, 187, 7, - 0, 205, 16, 102, 97, 144, 31, 7, 232, 96, 0, 195, 6, 30, 102, 96, -102, 190, 22, 13, 0, 0, 232, 79, 245, 102, 97, 144, 31, 7, 195, 6, - 30, 102, 96, 102, 190, 38, 13, 0, 0, 232, 60, 245, 102, 97, 144, 31, - 7, 195, 6, 30, 102, 96, 102, 190, 54, 13, 0, 0, 232, 41, 245, 102, - 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 190, 70, 13, 0, 0, 232, - 22, 245, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 190, 86, 13, - 0, 0, 232, 3, 245, 102, 97, 144, 31, 7, 195, 102, 80, 102, 184, 0, - 0, 245, 255, 102, 64, 102, 11, 192, 117, 249, 102, 88, 195, 102, 81, 102, - 80, 102, 184, 5, 0, 0, 0, 30, 7, 102, 139, 249, 232, 71, 252, 102, -139, 193, 102, 91, 102, 83, 102, 15, 183, 14, 12, 2, 102, 186, 14, 2, - 0, 0, 232, 68, 248, 102, 91, 102, 89, 102, 11, 192, 15, 133, 47, 0, -102, 139, 193, 102, 139, 203, 102, 80, 102, 83, 232, 35, 0, 102, 91, 102, - 95, 102, 11, 192, 15, 132, 23, 0, 30, 7, 232, 9, 252, 102, 139, 199, -102, 15, 183, 14, 12, 2, 102, 186, 14, 2, 0, 0, 232, 10, 248, 195, -102, 81, 102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, - 0, 0, 0, 0, 232, 242, 247, 102, 11, 192, 15, 132, 82, 0, 102, 139, -216, 30, 7, 102, 139, 62, 22, 2, 232, 135, 248, 30, 7, 102, 139, 30, - 22, 2, 102, 89, 38, 102, 57, 15, 15, 132, 46, 0, 38, 102, 131, 63, -255, 15, 132, 45, 0, 38, 131, 127, 4, 0, 15, 132, 36, 0, 38, 102, - 15, 183, 71, 4, 3, 216, 139, 195, 37, 0, 128, 116, 215, 140, 192, 5, - 0, 8, 142, 192, 129, 227, 255, 127, 235, 202, 38, 102, 139, 71, 16, 195, -102, 89, 102, 51, 192, 195, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 48, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 49, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 50, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 51, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 52, 13, 10 }; - diff --git a/ntfstools/dumplog.c b/ntfstools/dumplog.c deleted file mode 100644 index 1a730c97..00000000 --- a/ntfstools/dumplog.c +++ /dev/null @@ -1,310 +0,0 @@ -const char *EXEC_NAME = "dumplog"; -const char *EXEC_VERSION = "1.0"; -/* - * $Id$ - * - * DumpLog - Part of the Linux-NTFS project. - * - * Copyright (c) 2000,2001 Anton Altaparmakov. - * - * This utility will interpret the contents of the journal ($LogFile) specified - * on the command line and display the results on stdout. Errors will be output - * to stderr. - * - * Anton Altaparmakov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS source - * in the file COPYING); if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "types.h" -#include "mst.h" -#include "logfile.h" - -int main(int argc, char **argv) -{ - s64 l, br; - unsigned char *lfd = NULL; - RESTART_PAGE_HEADER *rph; - RESTART_AREA *rr; - RESTART_CLIENT *cr; - RECORD_PAGE_HEADER *rcrd_ph; - LOG_RECORD *lr; - int pass = 1; - int i, lps, client; - int f = 0; - char zero[4096]; - struct stat sbuf; - - memset(zero, 0, sizeof(zero)); - printf("\n"); - if (argc != 2) { - printf("%s v%s - Interpret and display information about the " - "journal\ngiven on the command line.\n\n" - /* Generic copyright / disclaimer. */ - "Copyright (c) 2001 Anton Altaparmakov.\n\n" - "%s is free software, released under the GNU " - "General Public License\nand you are welcome to " - "redistribute it under certain conditions.\n" - "%s comes with ABSOLUTELY NO WARRANTY; for details " - "read the GNU\nGeneral Public License to be found " - "in the file COPYING in the main Linux-NTFS\n" - "distribution directory.\n\n" - /* Generic part ends here. */ - "Syntax: dumplog log_file_name\n" - " e.g. dumplog /mnt/ntfstest/\\$LogFile\n\n", - EXEC_NAME, EXEC_VERSION, EXEC_NAME, EXEC_NAME); - fprintf(stderr, "Error: incorrect syntax\n"); - exit(1); - } - if (stat(argv[1], &sbuf) == -1) { - if (errno == ENOENT) - fprintf(stderr, "The file doesn't exist; did you " - "specify it correctly?\n"); - else - fprintf(stderr, "Error getting information about %s: " - "%s\n", argv[1], strerror(errno)); - exit(1); - } - f = open(argv[1], O_RDONLY); - if (f == -1) { - perror("Couldn't open file"); - exit(1); - } - l = sbuf.st_size; - if (l > 0x400000LL) { - printf("Only analysing the first four megabytes of the " - "logfile (real size = 0x%Lx).\n", - (unsigned long long)l); - l = 0x400000LL; - } - lfd = (unsigned char*)calloc(1, l); - if (!lfd) { - perror("Couldn't allocate internal buffer"); - goto log_file_error; - } - /* Read in the $LogFile into the buffer. */ - if ((br = read(f, lfd, l)) == -1) { - perror("Couldn't read file"); - goto log_file_error; - } - /* Valid data length in buffer. */ - l = min(br, l); - /* Check restart area. */ - if (!is_rstr_recordp(lfd)) { - s64 _l; - - for (_l = 0LL; _l < l; _l++) - if (lfd[_l] != (unsigned char)-1) - break; - if (_l < l) - puts("Logfile contents are corrupt (magic RSTR " - "missing)!"); - else - puts("Logfile is empty."); - goto log_file_error; - } - /* Do the interpretation and display now. */ - rph = (RESTART_PAGE_HEADER*)lfd; - lps = le32_to_cpu(rph->log_page_size); -pass_loc: - if (ntfs_post_read_mst_fixup((NTFS_RECORD*)rph, lps) || - is_baad_record(rph->magic)) { - puts("Logfile incomplete multi sector transfer detected! " - "Cannot handle this yet!"); - goto log_file_error; - } - if ((pass == 2) && !memcmp(lfd, rph, lps)) { - printf("2nd restart area fully matches the 1st one. Skipping " - "display.\n"); - goto skip_rstr_pass; - } - if (le16_to_cpu(rph->major_ver != 1) || - le16_to_cpu(rph->minor_ver != 1)) { - fprintf(stderr, "$LogFile version %i.%i! Error: Unknown " - "$LogFile version!\n", - le16_to_cpu(rph->major_ver), - le16_to_cpu(rph->minor_ver)); - goto log_file_error; - } - rr = (RESTART_AREA*)((char*)rph + le16_to_cpu(rph->restart_offset)); - cr = (RESTART_CLIENT*)((char*)rr + - le16_to_cpu(rr->client_array_offset)); - /* Dump of the interpreted $LogFile restart area. */ - if (pass == 1) - printf("\n$LogFile version %i.%i.\n", - le16_to_cpu(rph->major_ver), - le16_to_cpu(rph->minor_ver)); - printf("\n%s restart area:\n", pass == 1? "1st": "2nd"); - printf("magic = RSTR\n"); - printf("ChkDskLsn = 0x%Lx\n", sle64_to_cpu(rph->chkdsk_lsn)); - printf("SystemPageSize = %u\n", le32_to_cpu(rph->system_page_size)); - printf("LogPageSize = %u\n", le32_to_cpu(rph->log_page_size)); - printf("RestartOffset = 0x%x\n", le16_to_cpu(rph->restart_offset)); - printf("\n(1st) restart record:\n"); - printf("CurrentLsn = %Lx\n", sle64_to_cpu(rr->current_lsn)); - printf("LogClients = %u\n", le16_to_cpu(rr->log_clients)); - printf("ClientFreeList = %i\n", sle16_to_cpu(rr->client_free_list)); - printf("ClientInUseList = %i\n", sle16_to_cpu(rr->client_in_use_list)); - printf("Flags = 0x%x\n", le16_to_cpu(rr->flags)); - printf("SeqNumberBits = %u (0x%x)\n", le32_to_cpu(rr->seq_number_bits), - le32_to_cpu(rr->seq_number_bits)); - printf("RestartAreaLength = 0x%x\n", - le16_to_cpu(rr->restart_area_length)); - printf("ClientArrayOffset = 0x%x\n", - le16_to_cpu(rr->client_array_offset)); - printf("FileSize = %Lu (0x%Lx)\n", le64_to_cpu(rr->file_size), - le64_to_cpu(rr->file_size)); - printf("LastLsnDataLength = 0x%x\n", - le32_to_cpu(rr->last_lsn_data_length)); - printf("RecordLength = 0x%x\n", le16_to_cpu(rr->record_length)); - printf("LogPageDataOffset = 0x%x\n", - le16_to_cpu(rr->log_page_data_offset)); - for (client = 0; client < le16_to_cpu(rr->log_clients); client++) { - printf("\nRestart client record number %i:\n", client); - printf("OldestLsn = 0x%Lx\n", sle64_to_cpu(cr->oldest_lsn)); - printf("ClientRestartLsn = 0x%Lx\n", - sle64_to_cpu(cr->client_restart_lsn)); - printf("PrevClient = %i\n", sle16_to_cpu(cr->prev_client)); - printf("NextClient = %i\n", sle16_to_cpu(cr->next_client)); - printf("SeqNumber = 0x%Lx\n", le64_to_cpu(cr->seq_number)); - printf("ClientNameLength = 0x%x\n", - le32_to_cpu(cr->client_name_length)); - if (le32_to_cpu(cr->client_name_length)) { - // convert to ascii and print out. - // printf("ClientName = %u\n", le16_to_cpu(cr->client_name)); - } - /* Size of a restart client record is fixed at 0xa0 bytes. */ - cr = (RESTART_CLIENT*)((char*)cr + 0xa0); - } -skip_rstr_pass: - if (pass == 1) { - rph = (RESTART_PAGE_HEADER*)((char*)rph + lps); - ++pass; - goto pass_loc; - } - rcrd_ph = (RECORD_PAGE_HEADER*)rph; - /* Reuse pass for log record clienter. */ - pass = 0; - printf("\nFinished with restart area. Beginning with log area.\n"); -rcrd_pass_loc: - rcrd_ph = (RECORD_PAGE_HEADER*)((char*)rcrd_ph + lps); - if ((char*)rcrd_ph + lps > (char*)lfd + l) - goto end_of_rcrd_passes; - printf("\nLog record page number %i", pass); - if (!is_rcrd_record(rcrd_ph->magic)) { - for (i = 0; i < lps; i++) - if (((char*)rcrd_ph)[i] != (char)-1) - break; - if (i < lps) - puts(" is corrupt (magic RCRD is missing)."); - else - puts(" is empty."); - pass++; - goto rcrd_pass_loc; - } else - printf(":"); - /* Dump log record page */ - printf("\nmagic = RCRD\n"); - printf("copy.last_lsn/file_offset = 0x%Lx\n", - le64_to_cpu(rcrd_ph->copy.last_lsn)); - printf("flags = 0x%x\n", le32_to_cpu(rcrd_ph->flags)); - printf("page count = %i\n", le16_to_cpu(rcrd_ph->page_count)); - printf("page position = %i\n", le16_to_cpu(rcrd_ph->page_position)); - printf("header.next_record_offset = 0x%Lx\n", - le64_to_cpu(rcrd_ph->header.packed.next_record_offset)); - printf("header.last_end_lsn = 0x%Lx\n", - le64_to_cpu(rcrd_ph->header.packed.last_end_lsn)); - /* - * Where does the 0x40 come from? Is it just usa_offset + - * usa_client * 2 + 7 & ~7 or is it derived from somewhere? - */ - lr = (LOG_RECORD*)((char*)rcrd_ph + 0x40); - client = 0; -log_record_pass: - printf("\nLog record %i:\n", client); - printf("this lsn = 0x%Lx\n", le64_to_cpu(lr->this_lsn)); - printf("client previous lsn = 0x%Lx\n", - le64_to_cpu(lr->client_previous_lsn)); - printf("client undo next lsn = 0x%Lx\n", - le64_to_cpu(lr->client_undo_next_lsn)); - printf("client data length = 0x%x\n", - le32_to_cpu(lr->client_data_length)); - printf("client_id.seq_number = 0x%x\n", - le16_to_cpu(lr->client_id.seq_number)); - printf("client_id.client_index = 0x%x\n", - le16_to_cpu(lr->client_id.client_index)); - printf("record type = 0x%x\n", le32_to_cpu(lr->record_type)); - printf("transaction_id = 0x%x\n", le32_to_cpu(lr->transaction_id)); - printf("flags = 0x%x:", lr->flags); - if (!lr->flags) - printf(" NONE\n"); - else { - int _b = 0; - - if (lr->flags & LOG_RECORD_MULTI_PAGE) { - printf(" LOG_RECORD_MULTI_PAGE"); - _b = 1; - } - if (lr->flags & ~LOG_RECORD_MULTI_PAGE) { - if (_b) - printf(" |"); - printf(" Unknown flags"); - } - printf("\n"); - } - printf("redo_operation = 0x%x\n", le16_to_cpu(lr->redo_operation)); - printf("undo_operation = 0x%x\n", le16_to_cpu(lr->undo_operation)); - printf("redo_offset = 0x%x\n", le16_to_cpu(lr->redo_offset)); - printf("redo_length = 0x%x\n", le16_to_cpu(lr->redo_length)); - printf("undo_offset = 0x%x\n", le16_to_cpu(lr->undo_offset)); - printf("undo_length = 0x%x\n", le16_to_cpu(lr->undo_length)); - printf("target_attribute = 0x%x\n", le16_to_cpu(lr->target_attribute)); - printf("lcns_to_follow = 0x%x\n", le16_to_cpu(lr->lcns_to_follow)); - printf("record_offset = 0x%x\n", le16_to_cpu(lr->record_offset)); - printf("attribute_offset = 0x%x\n", le16_to_cpu(lr->attribute_offset)); - printf("target_vcn = 0x%Lx\n", sle64_to_cpu(lr->target_vcn)); - if (le16_to_cpu(lr->lcns_to_follow) > 0) - printf("Array of lcns:\n"); - for (i = 0; i < le16_to_cpu(lr->lcns_to_follow); i++) - printf("lcn_list[%i].lcn = 0x%Lx\n", i, - sle64_to_cpu(lr->lcn_list[i].lcn)); - client++; - lr = (LOG_RECORD*)((char*)lr + 0x70); - if (((char*)lr + 0x70 <= (char*)rcrd_ph + - le64_to_cpu(rcrd_ph->header.packed.next_record_offset))) - goto log_record_pass; - pass++; - goto rcrd_pass_loc; -end_of_rcrd_passes: -log_file_error: - printf("\n"); - /* Set return code to 0. */ - i = 0; - if (lfd) - free(lfd); - if (f) - close(f); - return i; -} - diff --git a/ntfstools/mkntfs.8.in b/ntfstools/mkntfs.8.in deleted file mode 100644 index 6916396d..00000000 --- a/ntfstools/mkntfs.8.in +++ /dev/null @@ -1,213 +0,0 @@ -.\" -*- nroff -*- -.\" Copyright (c) 2001,2002 Anton Altaparmakov. All Rights Reserved. -.\" This file may be copied under the terms of the GNU Public License. -.\" Adapted from e2fsprogs-1.19/misc/mke2fs.8.in by Theodore Ts'o. -.\" -.TH MKNTFS 8 "March 2002" "Linux-NTFS version @VERSION@" -.SH NAME -mkntfs \- create a NTFS 1.2 (Windows NT/2000/XP) file system -.SH SYNOPSIS -.B mkntfs -[ -.B \-s -.I sector-size -] -[ -.B \-c -.I cluster-size -] -[ -.B \-L -.I volume-label -] -[ -.B \-z -.I mft-zone-multiplier -] -[ -.B \-f -| -.B \-Q -] -[ -.B -n -] -[ -.B \-q -] -[ -.B \-v -] -[ -.B \-vv -] -[ -.B \-C -] -[ -.B \-F -] -[ -.B \-I -] -[ -.B \-V -] -.I device -[ -.I number-of-sectors -] -.SH DESCRIPTION -.B mkntfs -is used to create a NTFS 1.2 (Windows NT 4.0) file system on a device (usually -a disk partition). -.I device -is the special file corresponding to the device (e.g -.IR /dev/hdXX ). -.I number-of-sectors -is the number of blocks on the device. If omitted, -.B mkntfs -automagically figures the file system size. -.SH OPTIONS -.TP -.BI \-s " sector-size" -Specify the size of sectors in bytes. Valid sector size values are 256, 512, -1024, 2048 and 4096 bytes per sector. If omitted, -.B mkntfs -.I sector-size -is determined automatically and if that fails a default of 512 -bytes per sector is used. -.TP -.BI \-c " cluster-size" -Specify the size of clusters in bytes. Valid cluster size values are powers of -two, with at least 256, and at most 65536 bytes per cluster. If omitted, -.B mkntfs -.I cluster-size -is determined by the volume size. The value is determined as -follows: -.TS -lB lB lB -l l r. -Volume size Default cluster -0 - 512MB 512 bytes -512MB - 1GB 1024 bytes -1GB - 2GB 2048 bytes -2GB + 4096 bytes -.TE - -Note that the default cluster size is set to be at least equal to the sector -size as a cluster cannot be smaller than a sector. Also, note that values -greater than 4096 have the side effect that compression is disabled on the -volume (due to limitations in the NTFS compression algorithm currently in use -by Windows). -.TP -.BI \-L " volume-label" -Set the volume label for the filesystem. -.TP -.BI \-z " mft-zone-multiplier" -Set the MFT zone multiplier, which determines the size of the MFT zone to use -on the volume. The MFT zone is the area at the beginning of the volume reserved -for the master file table (MFT), which stores the on disk inodes (MFT records). -It is noteworthy that small files are stored entirely within the inode; -thus, if you expect to use the volume for storing large numbers of very small -files, it is useful to set the zone multiplier to a higher value. Note, that -the MFT zone is resized on the fly as required during operation of the NTFS -driver but choosing a good value will reduce fragmentation. Valid values -are 1, 2, 3 and 4. The values have the following meaning: -.TS -lB lB -lB lB -c l. -MFT zone MFT zone size -multiplier (% of volume size) -1 12.5% (default) -2 25.0% -3 37.5% -4 50.0% -.TE -.TP -.B \-f -Same as -.BR \-Q . -.TP -.B \-Q -Perform quick format. This will skip both zeroing of the volume and bad sector -checking. -.TP -.B \-n -Causes -.B mkntfs -to not actually create a filesystem, but display what it would do if it were -to create a filesystem. All steps of the format are carried out except the -actual writing to the device. -.TP -.B \-q -Quiet execution; only errors are written to stderr, no output to stdout -occurs at all. Useful if -.B mkntfs -is run in a script. -.TP -.B \-v -Verbose execution. -.TP -.B \-vv -Really verbose execution; includes the verbose output from the -.B \-v -option as well as additional output useful for debugging -.B mkntfs. -.TP -.B \-C -Enable compression on the volume. -.TP -.B \-F -Force -.B mkntfs -to run, even if the specified -.I device -is not a block special device, or appears to be mounted. -.TP -.B \-I -Disable content indexing on the volume. (This is only meaningful on -Windows 2000 and later. Windows NT 4.0 and earlier ignore this as they do -not implement content indexing at all.) -.TP -.B \-V -Print the version number of -.B mkntfs -and exit. -.SH AUTHOR -This version of -.B mkntfs -has been written by Anton Altaparmakov (if that fails, use -). -.SH BUGS -.B mkntfs -writes the backup boot sector to the last sector of the block -.I device -being formatted. However, current versions of the Linux kernel (all versions -up to and including todays 2.4.18) either only report an even number of sectors -when the sector size is below 1024 bytes, which is the case for most hard -drives today (512 bytes sector size) or they return the correct number but -accessing the last sector fails. Either way, this means that when a partition -has an odd number of 512-byte sectors, the last sector is either not reported -to us at all or it is not writable by us and hence the created NTFS volume -will either have the backup boot sector placed one sector ahead of where it -should be or it cannot be written at all. For this reason, -.B mkntfs -marks the NTFS volume dirty, so that when you reboot into Windows, check disk -runs automatically and creates a copy of the backup boot sector in the correct -location. This also has the benefit of catching any bugs in -.B mkntfs -as check disk would find any corrupt structures and repair them, as well as -report them. - If you do see any problems reported, please report the messages -to the author. -.br -There may be other bugs. Please, report them to the author. -.SH AVAILABILITY -.B mkntfs -is part of the Linux-NTFS project and is available for download from -http://sf.net/project/showfiles.php?group_id=13956 in source (tar ball and -rpm) and pre-compiled binary (i386 rpm and deb) form. -.SH SEE ALSO -.BR badblocks (8) - diff --git a/ntfstools/mkntfs.c b/ntfstools/mkntfs.c deleted file mode 100644 index 47357c76..00000000 --- a/ntfstools/mkntfs.c +++ /dev/null @@ -1,3554 +0,0 @@ -/* - * $Id$ - * - * mkntfs - Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2002 Anton Altaparmakov. - * Copyright (C) 2001-2002 Richard Russon. - * - * This utility will create an NTFS 1.2 (Windows NT 4.0) volume on a user - * specified (block) device. - * - * Some things (option handling and determination of mount status) have been - * adapted from e2fsprogs-1.19 and lib/ext2fs/ismounted.c and misc/mke2fs.c in - * particular. - * - * Anton Altaparmakov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS source - * in the file COPYING); if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * WARNING: This program might not work on architectures which do not allow - * unaligned access. For those, the program would need to start using - * get/put_unaligned macros (#include ), but not doing it yet, - * since NTFS really mostly applies to ia32 only, which does allow unaligned - * accesses. We might not actually have a problem though, since the structs are - * defined as being packed so that might be enough for gcc to insert the - * correct code. - * - * If anyone using a non-little endian and/or an aligned access only CPU tries - * this program please let me know whether it works or not! - * - * Anton Altaparmakov - */ - -#include "config.h" - -#ifdef HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_STDLIB_H -# include -#endif -#ifdef HAVE_STDIO_H -# include -#endif -#ifdef HAVE_STDARG_H -# include -#endif -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_ERRNO_H -# include -#endif -#include -#ifdef HAVE_LINUX_FD_H -# include -# include -#endif -#include -#ifdef HAVE_GETOPT_H -# include -#else - extern char *optarg; - extern int optind; -#endif -#include -#ifdef HAVE_LINUX_MAJOR_H -# include -#endif -#ifndef MAJOR -# define MAJOR(dev) ((dev) >> 8) -# define MINOR(dev) ((dev) & 0xff) -#endif -#ifndef SCSI_BLK_MAJOR -# define SCSI_BLK_MAJOR(m) ((m) == SCSI_DISK_MAJOR || \ - (m) == SCSI_CDROM_MAJOR) -#endif -#include - -#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) -# define BLKGETSIZE _IO(0x12,96) /* Get device size in 512byte blocks. */ -#endif - -#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET) -# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytse. */ -#endif - -#include "types.h" -#include "bootsect.h" -#include "disk_io.h" -#include "attrib.h" -#include "bitmap.h" -#include "mst.h" -#include "dir.h" -#include "runlist.h" - -extern const unsigned char attrdef_ntfs12_array[2400]; -extern const unsigned char boot_array[3429]; -extern void init_system_file_sd(int sys_file_no, char **sd_val, - int *sd_val_len); -extern void init_upcase_table(uchar_t *uc, u32 uc_len); - -/* Page size on ia32. Can change to 8192 on Alpha. */ -#define NTFS_PAGE_SIZE 4096 - -const char *EXEC_NAME = "mkntfs"; - -/* Need these global so mkntfs_exit can access them. */ -struct flock flk; -char *buf = NULL; -char *buf2 = NULL; -int buf2_size = 0; -int mft_bitmap_size, mft_bitmap_byte_size; -unsigned char *mft_bitmap = NULL; -int lcn_bitmap_byte_size; -unsigned char *lcn_bitmap = NULL; -run_list *rl = NULL, *rl_mft = NULL, *rl_mft_bmp = NULL, *rl_mftmirr = NULL; -run_list *rl_logfile = NULL, *rl_boot = NULL, *rl_bad = NULL, *rl_index; -INDEX_ALLOCATION *index_block = NULL; -ntfs_volume *vol; - -struct { - int sector_size; /* -s, in bytes, power of 2, default is - 512 bytes. */ - long long nr_sectors; /* size of device in sectors */ - long long nr_clusters; /* Note: Win2k treats clusters as - 32-bit entities! */ - long long volume_size; /* in bytes, or suffixed - with k for kB, m or M for MB, or - g or G for GB, or t or T for TB */ - int index_block_size; /* in bytes. */ - int mft_size; /* The bigger of 16kB & one cluster. */ - long long mft_lcn; /* lcn of $MFT, $DATA attribute. */ - long long mftmirr_lcn; /* lcn of $MFTMirr, $DATA. */ - long long logfile_lcn; /* lcn of $LogFile, $DATA. */ - int logfile_size; /* in bytes, determined from - volume_size. */ - char mft_zone_multiplier; /* -z, value from 1 to 4. Default is - 1. */ - long long mft_zone_end; /* Determined from volume_size and - mft_zone_multiplier, in clusters. */ - char no_action; /* -n, do not write to device, only - display what would be done. */ - char check_bad_blocks; /* read-only test for bad - clusters. */ - long long *bad_blocks; /* Array of bad clusters. */ - long long nr_bad_blocks; /* Number of bad clusters. */ - char *bad_blocks_filename; /* filename, file to read list of - bad clusters from. */ - ATTR_DEF *attr_defs; /* filename, attribute defs. */ - int attr_defs_len; /* in bytes */ - uchar_t *upcase; /* filename, upcase table. */ - u32 upcase_len; /* Determined automatically. */ - char quiet; /* -q, quiet execution. */ - char verbose; /* -v, verbose execution, given twice, - * really verbose execution (debug - * mode). */ - char force; /* -F, force fs creation. */ - char quick_format; /* -f or -Q, fast format, don't zero - the volume first. */ - char enable_compression; /* -C, enables compression of all files - on the volume by default. */ - char disable_indexing; /* -I, disables indexing of file - contents on the volume by default. */ - /* -V, print version and exit. */ -} opt; - -/* Error output. Ignores quiet (-q). */ -void Eprintf(const char *fmt, ...) -{ - va_list ap; - - fprintf(stderr, "ERROR: "); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -void err_exit(const char *fmt, ...) __attribute__ ((noreturn)); - -/* Error output and terminate. Ignores quiet (-q). */ -void err_exit(const char *fmt, ...) -{ - va_list ap; - - fprintf(stderr, "ERROR: "); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "Aborting...\n"); - exit(1); -} - -/* Debugging output (-vv). Overriden by quiet (-q). */ -void Dprintf(const char *fmt, ...) -{ - va_list ap; - - if (!opt.quiet && opt.verbose > 1) { - printf("DEBUG: "); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - } -} - -/* Verbose output (-v). */ -void Vprintf(const char *fmt, ...) -{ - va_list ap; - - if (!opt.quiet && opt.verbose > 0) { - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - } -} - -/* Quietable output (if not -q). */ -void Qprintf(const char *fmt, ...) -{ - va_list ap; - - if (!opt.quiet) { - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - } -} - -void append_to_bad_blocks(unsigned long block) -{ - long long *new_buf; - - if (!(opt.nr_bad_blocks & 15)) { - new_buf = realloc(opt.bad_blocks, (opt.nr_bad_blocks + 16) * - sizeof(long long)); - if (!new_buf) - err_exit("Reallocating memory for bad blocks list " - "failed: %s\n", strerror(errno)); - if (opt.bad_blocks != new_buf) - free(opt.bad_blocks); - opt.bad_blocks = new_buf; - } - opt.bad_blocks[opt.nr_bad_blocks++] = block; -} - -__inline__ long long mkntfs_write(int fd, const void *buf, long long count) -{ - long long bytes_written, total; - int retry; - - if (opt.no_action) - return count; - total = 0LL; - retry = 0; - do { - bytes_written = write(fd, buf, count); - if (bytes_written == -1LL) { - retry = errno; - Eprintf("Error writing to %s: %s\n", vol->dev_name, - strerror(errno)); - errno = retry; - return bytes_written; - } else if (!bytes_written) - ++retry; - else { - count -= bytes_written; - total += bytes_written; - } - } while (count && retry < 3); - if (count) - Eprintf("Failed to complete writing to %s after three retries." - "\n", vol->dev_name); - return total; -} - -/* - * Write to disk the clusters contained in the run list @rl taking the data - * from @val. Take @val_len bytes from @val and pad the rest with zeroes. - * - * If the @rl specifies a completely sparse file, @val is allowed to be NULL. - * - * @inited_size if not NULL points to an output variable which will contain - * the actual number of bytes written to disk. I.e. this will not include - * sparse bytes for example. - * - * Return the number of bytes written (minus padding) or -1 on error. Errno - * will be set to the error code. - */ -s64 ntfs_rlwrite(int fd, const run_list *rl, const char *val, - const s64 val_len, s64 *inited_size) -{ - s64 bytes_written, total, length, delta; - int retry, i; - - if (inited_size) - *inited_size = 0LL; - if (opt.no_action) - return val_len; - total = delta = 0LL; - for (i = 0; rl[i].length; i++) { - length = rl[i].length * vol->cluster_size; - /* Don't write sparse runs. */ - if (rl[i].lcn == -1) { - total += length; - if (!val) - continue; - // TODO: Check that *val is really zero at pos and len. - continue; - } - if (lseek(fd, rl[i].lcn * vol->cluster_size, SEEK_SET) == - (off_t)-1) - return -1LL; - retry = 0; - do { - if (total + length > val_len) { - delta = length; - length = val_len - total; - delta -= length; - } - bytes_written = write(fd, val + total, length); - if (bytes_written == -1LL) { - retry = errno; - Eprintf("Error writing to %s: %s\n", - vol->dev_name, strerror(errno)); - errno = retry; - return bytes_written; - } - if (bytes_written) { - length -= bytes_written; - total += bytes_written; - if (inited_size) - *inited_size += bytes_written; - } else - ++retry; - } while (length && retry < 3); - if (length) { - Eprintf("Failed to complete writing to %s after three " - "retries.\n", vol->dev_name); - return total; - } - } - if (delta) { - char *buf = (char*)calloc(1, delta); - if (!buf) - err_exit("Error allocating internal buffer: " - "%s\n", strerror(errno)); - bytes_written = mkntfs_write(fd, buf, delta); - free(buf); - if (bytes_written == -1LL) - return bytes_written; - } - return total; -} - -/** - * ucslen - determine the length of a fixed-size unicode-character string - * @s: pointer to unicode-character string - * - * Return the number of unicode-characters in @s up to a maximum of maxlen - * unicode-characters, not including the terminating (uchar_t)'\0'. If there - * is no (uchar_t)'\0' between s and s+maxlen, maxlen is returned. - * - * This function never looks beyond s+maxlen. - */ -int ucsnlen(const uchar_t *s, int maxlen) -{ - int i; - - for (i = 0; i < maxlen; i++) - if (!s[i]) - break; - return i; -} - -/** - * ucstos - convert unicode-character string to ASCII - * @dest: points to buffer to receive the converted string - * @src: points to string to convert - * @maxlen: size of @dest buffer in bytes - * - * Return the number of characters written to @dest, not including the - * terminating null byte. If a unicode character was encountered which could - * not be converted -1 is returned. - */ -int ucstos(char *dest, const uchar_t *src, int maxlen) -{ - uchar_t u; - int i; - - /* Need one byte for null terminator. */ - maxlen--; - for (i = 0; i < maxlen; i++) { - u = le16_to_cpu(src[i]); - if (!u) - break; - if (u & 0xff00) - return -1; - dest[i] = u & 0xff; - } - dest[i] = 0; - return i; -} - -/** - * stoucs - convert ASCII string to unicode-character string - * @dest: points to buffer to receive the converted string - * @src: points to string to convert - * @maxlen: size of @dest buffer in bytes - * - * Return the number of characters written to @dest, not including the - * terminating null unicode character. - */ -int stoucs(uchar_t *dest, const char *src, int maxlen) -{ - char c; - int i; - - /* Need two bytes for null terminator. */ - maxlen -= 2; - for (i = 0; i < maxlen; i++) { - c = src[i]; - if (!c) - break; - dest[i] = cpu_to_le16(c); - } - dest[i] = cpu_to_le16('\0'); - return i; -} - -void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len) -{ - const char *don_t_know = "Don't know what to do with this attribute " - "type yet."; - const char *skip = "Skipping display of $%s attribute value.\n"; - const char *todo = "This is still work in progress."; - char *buf; - int i, j; - - switch (type) { - case AT_STANDARD_INFORMATION: - // TODO - printf("%s\n", todo); - return; - case AT_ATTRIBUTE_LIST: - // TODO - printf("%s\n", todo); - return; - case AT_FILE_NAME: - // TODO - printf("%s\n", todo); - return; - case AT_OBJECT_ID: - // TODO - printf("%s\n", todo); - return; - case AT_SECURITY_DESCRIPTOR: - // TODO - printf("%s\n", todo); - return; - case AT_VOLUME_NAME: - printf("Volume name length = %i\n", val_len); - if (val_len) { - buf = calloc(1, val_len); - if (!buf) - err_exit("Failed to allocate internal buffer: " - "%s\n", strerror(errno)); - i = ucstos(buf, (uchar_t*)val, val_len); - if (i == -1) - printf("Volume name contains non-displayable " - "Unicode characters.\n"); - printf("Volume name = %s\n", buf); - free(buf); - } - return; - case AT_VOLUME_INFORMATION: -#define VOL_INF(x) ((VOLUME_INFORMATION *)(x)) - printf("NTFS version %i.%i\n", VOL_INF(val)->major_ver, - VOL_INF(val)->minor_ver); - i = VOL_INF(val)->flags; -#undef VOL_INF - printf("Volume flags = 0x%x: ", i); - if (!i) { - printf("NONE\n"); - return; - } - j = 0; - if (i & VOLUME_MODIFIED_BY_CHKDSK) { - j = 1; - printf("VOLUME_MODIFIED_BY_CHKDSK"); - } - if (i & VOLUME_REPAIR_OBJECT_ID) { - if (j) - printf(" | "); - else - j = 0; - printf("VOLUME_REPAIR_OBJECT_ID"); - } - if (i & VOLUME_DELETE_USN_UNDERWAY) { - if (j) - printf(" | "); - else - j = 0; - printf("VOLUME_DELETE_USN_UNDERWAY"); - } - if (i & VOLUME_MOUNTED_ON_NT4) { - if (j) - printf(" | "); - else - j = 0; - printf("VOLUME_MOUNTED_ON_NT4"); - } - if (i & VOLUME_UPGRADE_ON_MOUNT) { - if (j) - printf(" | "); - else - j = 0; - printf("VOLUME_UPGRADE_ON_MOUNT"); - } - if (i & VOLUME_RESIZE_LOG_FILE) { - if (j) - printf(" | "); - else - j = 0; - printf("VOLUME_RESIZE_LOG_FILE"); - } - if (i & VOLUME_IS_DIRTY) { - if (j) - printf(" | "); - else - j = 0; - printf("VOLUME_IS_DIRTY"); - } - printf("\n"); - return; - case AT_DATA: - printf(skip, "DATA"); - return; - case AT_INDEX_ROOT: - // TODO - printf("%s\n", todo); - return; - case AT_INDEX_ALLOCATION: - // TODO - printf("%s\n", todo); - return; - case AT_BITMAP: - printf(skip, "BITMAP"); - return; - case AT_REPARSE_POINT: - // TODO - printf("%s\n", todo); - return; - case AT_EA_INFORMATION: - // TODO - printf("%s\n", don_t_know); - return; - case AT_EA: - // TODO - printf("%s\n", don_t_know); - return; - case AT_LOGGED_UTILITY_STREAM: - // TODO - printf("%s\n", don_t_know); - return; - default: - i = le32_to_cpu(type); - printf("Cannot display unknown %s defined attribute type 0x%x" - ".\n", i >= - le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ? - "user" : "system", i); - } -} - -void dump_resident_attr(ATTR_RECORD *a) -{ - int i; - - i = le32_to_cpu(a->value_length); - printf("Attribute value length = %u (0x%x)\n", i, i); - i = le16_to_cpu(a->value_offset); - printf("Attribute value offset = %u (0x%x)\n", i, i); - i = a->resident_flags; - printf("Resident flags = 0x%x: ", i); - if (!i) - printf("NONE\n"); - else if (i & ~RESIDENT_ATTR_IS_INDEXED) - printf("UNKNOWN FLAG(S)\n"); - else - printf("RESIDENT_ATTR_IS_INDEXED\n"); - dump_resident_attr_val(a->type, (char*)a + le16_to_cpu(a->value_offset), - le32_to_cpu(a->value_length)); -} - -void dump_mapping_pairs_array(char *b, unsigned int max_len) -{ - // TODO - return; -} - -void dump_non_resident_attr(ATTR_RECORD *a) -{ - s64 l; - int i; - - l = sle64_to_cpu(a->lowest_vcn); - printf("Lowest VCN = %Li (0x%Lx)\n", l, l); - l = sle64_to_cpu(a->highest_vcn); - printf("Highest VCN = %Li (0x%Lx)\n", l, l); - printf("Mapping pairs array offset = 0x%x\n", - le16_to_cpu(a->mapping_pairs_offset)); - printf("Compression unit = 0x%x: %sCOMPRESSED\n", a->compression_unit, - a->compression_unit ? "" : "NOT "); - if (sle64_to_cpu(a->lowest_vcn)) - printf("Attribute is not the first extent. The following " - "sizes are meaningless:\n"); - l = sle64_to_cpu(a->allocated_size); - printf("Allocated size = %Li (0x%Lx)\n", l, l); - l = sle64_to_cpu(a->data_size); - printf("Data size = %Li (0x%Lx)\n", l, l); - l = sle64_to_cpu(a->initialized_size); - printf("Initialized size = %Li (0x%Lx)\n", l, l); - if (a->flags & ATTR_COMPRESSION_MASK) { - l = sle64_to_cpu(a->compressed_size); - printf("Compressed size = %Li (0x%Lx)\n", l, l); - } - i = le16_to_cpu(a->mapping_pairs_offset); - dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i); -} - -void dump_attr_record(ATTR_RECORD *a) -{ - unsigned int u; - char s[0x200]; - int i; - - printf("-- Beginning dump of attribute record. --\n"); - if (a->type == AT_END) { - printf("Attribute type = 0x%x ($END)\n", le32_to_cpu(AT_END)); - u = le32_to_cpu(a->length); - printf("Length of resident part = %u (0x%x)\n", u, u); - return; - } - u = le32_to_cpu(a->type); - for (i = 0; opt.attr_defs[i].type; i++) - if (le32_to_cpu(opt.attr_defs[i].type) >= u) - break; - if (opt.attr_defs[i].type) { -// printf("type = 0x%x\n", le32_to_cpu(opt.attr_defs[i].type)); -// { char *p = (char*)opt.attr_defs[i].name; -// printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]); -// } - if (ucstos(s, opt.attr_defs[i].name, sizeof(s)) == -1) { - Eprintf("Could not convert Unicode string to single " - "byte string in current locale.\n"); - strncpy(s, "Error converting Unicode string", - sizeof(s)); - } - } else - strncpy(s, "UNKNOWN_TYPE", sizeof(s)); - printf("Attribute type = 0x%x (%s)\n", u, s); - u = le32_to_cpu(a->length); - printf("Length of resident part = %u (0x%x)\n", u, u); - printf("Attribute is %sresident\n", a->non_resident ? "non-" : ""); - printf("Name length = %u unicode characters\n", a->name_length); - printf("Name offset = %u (0x%x)\n", cpu_to_le16(a->name_offset), - cpu_to_le16(a->name_offset)); - u = a->flags; - if (a->name_length) { - if (ucstos(s, (uchar_t*)((char*)a + - cpu_to_le16(a->name_offset)), - min(sizeof(s), a->name_length + 1)) == -1) { - Eprintf("Could not convert Unicode string to single " - "byte string in current locale.\n"); - strncpy(s, "Error converting Unicode string", - sizeof(s)); - - } - printf("Name = %s\n", s); - } - printf("Attribute flags = 0x%x: ", le16_to_cpu(u)); - if (!u) - printf("NONE"); - else { - int first = TRUE; - if (u & ATTR_COMPRESSION_MASK) { - if (u & ATTR_IS_COMPRESSED) { - printf("ATTR_IS_COMPRESSED"); - first = FALSE; - } - if ((u & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) { - if (!first) - printf(" | "); - else - first = FALSE; - printf("ATTR_UNKNOWN_COMPRESSION"); - } - } - if (u & ATTR_IS_ENCRYPTED) { - if (!first) - printf(" | "); - else - first = FALSE; - printf("ATTR_IS_ENCRYPTED"); - } - if (u & ATTR_IS_SPARSE) { - if (!first) - printf(" | "); - else - first = FALSE; - printf("ATTR_IS_SPARSE"); - } - } - printf("\n"); - printf("Attribute instance = %u\n", le16_to_cpu(a->instance)); - if (a->non_resident) { - dump_non_resident_attr(a); - } else { - dump_resident_attr(a); - } -} - -void dump_mft_record(MFT_RECORD *m) -{ - ATTR_RECORD *a; - unsigned int u; - MFT_REF r; - - printf("-- Beginning dump of mft record. --\n"); - u = le32_to_cpu(m->magic); - printf("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, - u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); - u = le16_to_cpu(m->usa_ofs); - printf("Update sequence array offset = %u (0x%x)\n", u, u); - printf("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); - printf("$LogFile sequence number (lsn) = %Lu\n", le64_to_cpu(m->lsn)); - printf("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); - printf("Reference (hard link) count = %u\n", - le16_to_cpu(m->link_count)); - u = le16_to_cpu(m->attrs_offset); - printf("First attribute offset = %u (0x%x)\n", u, u); - printf("Flags = %u: ", le16_to_cpu(m->flags)); - if (m->flags & MFT_RECORD_IN_USE) - printf("MFT_RECORD_IN_USE"); - else - printf("MFT_RECORD_NOT_IN_USE"); - if (m->flags & MFT_RECORD_IS_DIRECTORY) - printf(" | MFT_RECORD_IS_DIRECTORY"); - printf("\n"); - u = le32_to_cpu(m->bytes_in_use); - printf("Bytes in use = %u (0x%x)\n", u, u); - u = le32_to_cpu(m->bytes_allocated); - printf("Bytes allocated = %u (0x%x)\n", u, u); - r = le64_to_cpu(m->base_mft_record); - printf("Base mft record reference:\n\tMft record number = %Lu\n\t" - "Sequence number = %u\n", MREF(r), MSEQNO(r)); - printf("Next attribute instance = %u\n", - le16_to_cpu(m->next_attr_instance)); - a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); - printf("-- Beginning dump of attributes within mft record. --\n"); - while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { - dump_attr_record(a); - if (a->type == AT_END) - break; - a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); - }; - printf("-- End of attributes. --\n"); -} - -void format_mft_record(MFT_RECORD *m) -{ - ATTR_RECORD *a; - - memset(m, 0, vol->mft_record_size); - m->magic = magic_FILE; - /* Aligned to 2-byte boundary. */ - m->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); - if (vol->mft_record_size >= NTFS_SECTOR_SIZE) - m->usa_count = cpu_to_le16(vol->mft_record_size / - NTFS_SECTOR_SIZE + 1); - else { - m->usa_count = cpu_to_le16(1); - Qprintf("Sector size is bigger than MFT record size. Setting " - "usa_count to 1. If Windows\nchkdsk reports this as " - "corruption, please email linux-ntfs-dev@lists.sf.net\n" - "stating that you saw this message and that the file " - "system created was corrupt.\nThank you."); - } - /* Set the update sequence number to 1. */ - *(u16*)((char*)m + ((sizeof(MFT_RECORD) + 1) & ~1)) = cpu_to_le16(1); - m->lsn = cpu_to_le64(0LL); - m->sequence_number = cpu_to_le16(1); - m->link_count = cpu_to_le16(0); - /* Aligned to 8-byte boundary. */ - m->attrs_offset = cpu_to_le16((le16_to_cpu(m->usa_ofs) + - (le16_to_cpu(m->usa_count) << 1) + 7) & ~7); - m->flags = cpu_to_le16(0); - /* - * Using attrs_offset plus eight bytes (for the termination attribute), - * aligned to 8-byte boundary. - */ - m->bytes_in_use = cpu_to_le32((le16_to_cpu(m->attrs_offset) + 8 + 7) & - ~7); - m->bytes_allocated = cpu_to_le32(vol->mft_record_size); - m->base_mft_record = cpu_to_le64((MFT_REF)0); - m->next_attr_instance = cpu_to_le16(0); - a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); - a->type = AT_END; - a->length = cpu_to_le32(0); -#if 0 - if (!opt.quiet && opt.verbose > 1) - dump_mft_record(m); -#endif -} - -/** - * make_room_for_attribute - make room for an attribute inside an mft record - * @m: mft record - * @pos: position at which to make space - * @size: byte size to make available at this position - * - * @pos points to the attribute in front of which we want to make space. - * - * Return 0 on success or -errno on error. Possible error codes are: - * - * -ENOSPC There is not enough space available to complete - * operation. The caller has to make space before calling - * this. - * -EINVAL Can only occur if mkntfs was compiled with -DEBUG. Means - * the input parameters were faulty. - */ -int make_room_for_attribute(MFT_RECORD *m, char *pos, const u32 size) -{ - u32 biu; - - if (!size) - return 0; -#ifdef DEBUG - /* - * Rigorous consistency checks. Always return -EINVAL even if more - * appropriate codes exist for simplicity of parsing the return value. - */ - if (size != ((size + 7) & ~7)) { - Eprintf("make_room_for_attribute() received non 8-byte aligned" - "size.\n"); - return -EINVAL; - } - if (!m || !pos) - return -EINVAL; - if (pos < (char*)m || pos + size < (char*)m || - pos > (char*)m + le32_to_cpu(m->bytes_allocated) || - pos + size > (char*)m + le32_to_cpu(m->bytes_allocated)) - return -EINVAL; - /* The -8 is for the attribute terminator. */ - if (pos - (char*)m > le32_to_cpu(m->bytes_in_use) - 8) - return -EINVAL; -#endif - biu = le32_to_cpu(m->bytes_in_use); - /* Do we have enough space? */ - if (biu + size > le32_to_cpu(m->bytes_allocated)) - return -ENOSPC; - /* Move everything after pos to pos + size. */ - memmove(pos + size, pos, biu - (pos - (char*)m)); - /* Update mft record. */ - m->bytes_in_use = cpu_to_le32(biu + size); - return 0; -} - -/* Return 0 on success and -errno on error. */ -int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a, - const u32 new_vsize) -{ - int new_alen, new_muse; - - /* New attribute length and mft record bytes used. */ - new_alen = (le32_to_cpu(a->length) - le32_to_cpu(a->value_length) + - new_vsize + 7) & ~7; - new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) + - new_alen; - /* Check for sufficient space. */ - if (new_muse > le32_to_cpu(m->bytes_allocated) ) { - // Aarrgghh! Need to make space. Probably want generic function - // for this as we need to call it from other places, too. - return -ENOTSUP; - } - /* Move attributes behind @a to their new location. */ - memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length), - le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) - - le32_to_cpu(a->length)); - /* Adjust @m to reflect change in used space. */ - m->bytes_in_use = cpu_to_le32(new_muse); - /* Adjust @a to reflect new value size. */ - a->length = cpu_to_le32(new_alen); - a->value_length = cpu_to_le32(new_vsize); - return 0; -} - -void deallocate_scattered_clusters(const run_list *rl) -{ - LCN j; - int i; - - if (!rl) - return; - /* Iterate over all runs in the run list @rl. */ - for (i = 0; rl[i].length; i++) { - /* Skip sparse runs. */ - if (rl[i].lcn == -1LL) - continue; - /* Deallocate the current run. */ - for (j = rl[i].lcn; j < rl[i].lcn + rl[i].length; j++) - ntfs_set_bit(lcn_bitmap, j, 0); - } -} - -/* - * Allocate @clusters and create a run list of the allocated clusters. - * - * Return the allocated run list. Caller has to free the run list when finished - * with it. - * - * On error return NULL and errno is set to the error code. - * - * TODO: We should be returning the size as well, but for mkntfs this is not - * necessary. - */ -run_list *allocate_scattered_clusters(s64 clusters) -{ - run_list *rl = NULL, *rlt; - VCN vcn = 0LL; - LCN lcn, end, prev_lcn = 0LL; - int rlpos = 0; - int rlsize = 0; - s64 prev_run_len = 0LL; - char bit; - - end = opt.nr_clusters; - /* Loop until all clusters are allocated. */ - while (clusters) { - /* Loop in current zone until we run out of free clusters. */ - for (lcn = opt.mft_zone_end; lcn < end; lcn++) { - bit = ntfs_get_and_set_bit(lcn_bitmap, lcn, 1); - if (bit) - continue; - /* - * Reallocate memory if necessary. Make sure we have - * enough for the terminator entry as well. - */ - if ((rlpos + 2) * sizeof(run_list) >= rlsize) { - rlsize += 4096; /* PAGE_SIZE */ - rlt = realloc(rl, rlsize); - if (!rlt) - goto err_end; - rl = rlt; - } - /* Coalesce with previous run if adjacent LCNs. */ - if (prev_lcn == lcn - prev_run_len) { - rl[rlpos - 1].length = ++prev_run_len; - vcn++; - } else { - rl[rlpos].vcn = vcn++; - rl[rlpos].lcn = prev_lcn = lcn; - rl[rlpos].length = prev_run_len = 1LL; - rlpos++; - } - /* Done? */ - if (!--clusters) { - /* Add terminator element and return. */ - rl[rlpos].vcn = vcn; - rl[rlpos].lcn = rl[rlpos].length = 0LL; - return rl; - } - - } - /* Switch to next zone, decreasing mft zone by factor 2. */ - end = opt.mft_zone_end; - opt.mft_zone_end >>= 1; - /* Have we run out of space on the volume? */ - if (opt.mft_zone_end <= 0) - goto err_end; - } - return rl; -err_end: - if (rl) { - /* Add terminator element. */ - rl[rlpos].vcn = vcn; - rl[rlpos].lcn = -1LL; - rl[rlpos].length = 0LL; - /* Deallocate all allocated clusters. */ - deallocate_scattered_clusters(rl); - /* Free the run list. */ - free(rl); - } - return NULL; -} - -/* - * Create a non-resident attribute with a predefined on disk location - * specified by the run_list @rl. The clusters specified by @rl are assumed to - * be allocated already. - * - * Return 0 on success and -errno on error. - */ -int insert_positioned_attr_in_mft_record(MFT_RECORD *m, const ATTR_TYPES type, - const char *name, u32 name_len, const IGNORE_CASE_BOOL ic, - const ATTR_FLAGS flags, const run_list *rl, - const char *val, const s64 val_len) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - u16 hdr_size; - int asize, mpa_size, err, i; - s64 bw = 0, inited_size; - VCN highest_vcn; - uchar_t *uname; -/* - if (base record) - lookup_attr(); - else -*/ - if (name_len) { - i = (name_len + 1) * sizeof(uchar_t); - uname = (uchar_t*)calloc(1, i); - if (!uname) - return -errno; - name_len = stoucs(uname, name, i); - if (name_len > 0xff) { - free(uname); - return -ENAMETOOLONG; - } - } else - uname = NULL; - /* Check if the attribute is already there. */ - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) { - Eprintf("Failed to allocate attribute search context.\n"); - err = -ENOMEM; - goto err_out; - } - if (ic == IGNORE_CASE) { - Eprintf("FIXME: Hit unimplemented code path #1.\n"); - err = -ENOTSUP; - goto err_out; - } - if (!ntfs_lookup_attr(type, uname, name_len, ic, 0, NULL, 0, ctx)) { - err = -EEXIST; - goto err_out; - } - if (errno != ENOENT) { - Eprintf("Corrupt inode.\n"); - err = -errno; - goto err_out; - } - a = ctx->attr; - if (flags & ATTR_COMPRESSION_MASK) { - Eprintf("Compressed attributes not supported yet.\n"); - // FIXME: Compress attribute into a temporary buffer, set - // val accordingly and save the compressed size. - err = -ENOTSUP; - goto err_out; - } - if (flags & (ATTR_IS_ENCRYPTED || ATTR_IS_SPARSE)) { - Eprintf("Encrypted/sparse attributes not supported yet.\n"); - err = -ENOTSUP; - goto err_out; - } - if (flags & ATTR_COMPRESSION_MASK) { - hdr_size = 72; - // FIXME: This compression stuff is all wrong. Never mind for - // now. (AIA) - if (val_len) - mpa_size = 0; //get_size_for_compressed_mapping_pairs(rl); - else - mpa_size = 0; - } else { - hdr_size = 64; - if (val_len) { - mpa_size = ntfs_get_size_for_mapping_pairs(vol, rl); - if (mpa_size < 0) { - err = -errno; - Eprintf("Failed to get size for mapping " - "pairs.\n"); - goto err_out; - } - } else - mpa_size = 0; - } - /* Mapping pairs array and next attribute must be 8-byte aligned. */ - asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; - /* Get the highest vcn. */ - for (i = 0, highest_vcn = 0LL; rl[i].length; i++) - highest_vcn += rl[i].length; - /* Does the value fit inside the allocated size? */ - if (highest_vcn * vol->cluster_size < val_len) { - Eprintf("BUG: Allocated size is smaller than data size!\n"); - err = -EINVAL; - goto err_out; - } - err = make_room_for_attribute(m, (char*)a, asize); - if (err == -ENOSPC) { - // FIXME: Make space! (AIA) - // can we make it non-resident? if yes, do that. - // does it fit now? yes -> do it. - // m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? - // yes -> make non-resident - // does it fit now? yes -> do it. - // make all attributes non-resident - // does it fit now? yes -> do it. - // m is a base record? yes -> allocate extension record - // does the new attribute fit in there? yes -> do it. - // split up run_list into extents and place each in an extension - // record. - // FIXME: the check for needing extension records should be - // earlier on as it is very quick: asize > m->bytes_allocated? - err = -ENOTSUP; - goto err_out; - } -#ifdef DEBUG - else if (err == -EINVAL) { - fprintf(stderr, "BUG(): in insert_positioned_attribute_in_mft_" - "record(): make_room_for_attribute() returned " - "error: EINVAL!\n"); - goto err_out; - } -#endif - a->type = type; - a->length = cpu_to_le32(asize); - a->non_resident = 1; - a->name_length = name_len; - a->name_offset = cpu_to_le16(hdr_size); - a->flags = flags; - a->instance = m->next_attr_instance; - m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) - + 1) & 0xffff); - a->lowest_vcn = cpu_to_le64(0); - a->highest_vcn = cpu_to_le64(highest_vcn - 1LL); - a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); - memset(a->reserved1, 0, sizeof(a->reserved1)); - // FIXME: Allocated size depends on compression. - a->allocated_size = cpu_to_le64(highest_vcn * vol->cluster_size); - a->data_size = cpu_to_le64(val_len); - if (name_len) - memcpy((char*)a + hdr_size, uname, name_len << 1); - if (flags & ATTR_COMPRESSION_MASK) { - if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { - Eprintf("Unknown compression format. Reverting to " - "standard compression.\n"); - a->flags &= ~ATTR_COMPRESSION_MASK; - a->flags |= ATTR_IS_COMPRESSED; - } - a->compression_unit = 4; - inited_size = val_len; - // FIXME: Set the compressed size. - a->compressed_size = cpu_to_le64(0); - // FIXME: Write out the compressed data. - // FIXME: err = build_mapping_pairs_compressed(); - err = -ENOTSUP; - } else { - a->compression_unit = 0; - bw = ntfs_rlwrite(vol->fd, rl, val, val_len, &inited_size); - if (bw != val_len) - Eprintf("Error writing non-resident attribute value." - "\n"); - err = ntfs_build_mapping_pairs(vol, (s8*)a + hdr_size + - ((name_len + 7) & ~7), mpa_size, rl); - } - a->initialized_size = cpu_to_le64(inited_size); - if (err < 0 || bw != val_len) { - // FIXME: Handle error. - // deallocate clusters - // remove attribute - if (err >= 0) - err = -EIO; - Eprintf("insert_positioned_attr_in_mft_record failed with " - "error %i.\n", err < 0 ? err : bw); - } -err_out: - if (ctx) - ntfs_put_attr_search_ctx(ctx); - if (uname) - free(uname); - return err; -} - -/* Return 0 on success and -errno on error. */ -int insert_non_resident_attr_in_mft_record(MFT_RECORD *m, const ATTR_TYPES type, - const char *name, u32 name_len, const IGNORE_CASE_BOOL ic, - const ATTR_FLAGS flags, const char *val, const s64 val_len) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - u16 hdr_size; - int asize, mpa_size, err, i; - run_list *rl = NULL; - s64 bw = 0; - uchar_t *uname; -/* - if (base record) - lookup_attr(); - else -*/ - if (name_len) { - i = (name_len + 1) * sizeof(uchar_t); - uname = (uchar_t*)calloc(1, i); - if (!uname) - return -errno; - name_len = stoucs(uname, name, i); - if (name_len > 0xff) { - free(uname); - return -ENAMETOOLONG; - } - } else - uname = AT_UNNAMED; - /* Check if the attribute is already there. */ - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) { - Eprintf("Failed to allocate attribute search context.\n"); - err = -ENOMEM; - goto err_out; - } - if (ic == IGNORE_CASE) { - Eprintf("FIXME: Hit unimplemented code path #2.\n"); - err = -ENOTSUP; - goto err_out; - } - if (!ntfs_lookup_attr(type, uname, name_len, ic, 0, NULL, 0, ctx)) { - err = -EEXIST; - goto err_out; - } - if (errno != ENOENT) { - Eprintf("Corrupt inode.\n"); - err = -errno; - goto err_out; - } - a = ctx->attr; - if (flags & ATTR_COMPRESSION_MASK) { - Eprintf("Compressed attributes not supported yet.\n"); - // FIXME: Compress attribute into a temporary buffer, set - // val accordingly and save the compressed size. - err = -ENOTSUP; - goto err_out; - } - if (flags & (ATTR_IS_ENCRYPTED || ATTR_IS_SPARSE)) { - Eprintf("Encrypted/sparse attributes not supported yet.\n"); - err = -ENOTSUP; - goto err_out; - } - if (val_len) { - rl = allocate_scattered_clusters((val_len + - vol->cluster_size - 1) / vol->cluster_size); - if (!rl) { - err = -errno; - Eprintf("Failed to allocate scattered clusters: %s\n", - strerror(-err)); - goto err_out; - } - } else - rl = NULL; - if (flags & ATTR_COMPRESSION_MASK) { - hdr_size = 72; - // FIXME: This compression stuff is all wrong. Never mind for - // now. (AIA) - if (val_len) - mpa_size = 0; //get_size_for_compressed_mapping_pairs(rl); - else - mpa_size = 0; - } else { - hdr_size = 64; - if (val_len) { - mpa_size = ntfs_get_size_for_mapping_pairs(vol, rl); - if (mpa_size < 0) { - err = -errno; - Eprintf("Failed to get size for mapping " - "pairs.\n"); - goto err_out; - } - } else - mpa_size = 0; - } - /* Mapping pairs array and next attribute must be 8-byte aligned. */ - asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; - err = make_room_for_attribute(m, (char*)a, asize); - if (err == -ENOSPC) { - // FIXME: Make space! (AIA) - // can we make it non-resident? if yes, do that. - // does it fit now? yes -> do it. - // m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? - // yes -> make non-resident - // does it fit now? yes -> do it. - // make all attributes non-resident - // does it fit now? yes -> do it. - // m is a base record? yes -> allocate extension record - // does the new attribute fit in there? yes -> do it. - // split up run_list into extents and place each in an extension - // record. - // FIXME: the check for needing extension records should be - // earlier on as it is very quick: asize > m->bytes_allocated? - err = -ENOTSUP; - goto err_out; - } -#ifdef DEBUG - else if (err == -EINVAL) { - fprintf(stderr, "BUG(): in insert_non_resident_attribute_in_" - "mft_record(): make_room_for_attribute() " - "returned error: EINVAL!\n"); - goto err_out; - } -#endif - a->type = type; - a->length = cpu_to_le32(asize); - a->non_resident = 1; - a->name_length = name_len; - a->name_offset = cpu_to_le16(hdr_size); - a->flags = flags; - a->instance = m->next_attr_instance; - m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) - + 1) & 0xffff); - a->lowest_vcn = cpu_to_le64(0); - for (i = 0; rl[i].length; i++) - ; - a->highest_vcn = cpu_to_le64(rl[i].vcn - 1); - a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); - memset(a->reserved1, 0, sizeof(a->reserved1)); - // FIXME: Allocated size depends on compression. - a->allocated_size = cpu_to_le64((val_len + (vol->cluster_size - 1)) & - ~(vol->cluster_size - 1)); - a->data_size = cpu_to_le64(val_len); - a->initialized_size = cpu_to_le64(val_len); - if (name_len) - memcpy((char*)a + hdr_size, uname, name_len << 1); - if (flags & ATTR_COMPRESSION_MASK) { - if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { - Eprintf("Unknown compression format. Reverting to " - "standard compression.\n"); - a->flags &= ~ATTR_COMPRESSION_MASK; - a->flags |= ATTR_IS_COMPRESSED; - } - a->compression_unit = 4; - // FIXME: Set the compressed size. - a->compressed_size = cpu_to_le64(0); - // FIXME: Write out the compressed data. - // FIXME: err = build_mapping_pairs_compressed(); - err = -ENOTSUP; - } else { - a->compression_unit = 0; - bw = ntfs_rlwrite(vol->fd, rl, val, val_len, NULL); - if (bw != val_len) - Eprintf("Error writing non-resident attribute value." - "\n"); - err = ntfs_build_mapping_pairs(vol, (s8*)a + hdr_size + - ((name_len + 7) & ~7), mpa_size, rl); - } - if (err < 0 || bw != val_len) { - // FIXME: Handle error. - // deallocate clusters - // remove attribute - if (err >= 0) - err = -EIO; - Eprintf("insert_non_resident_attr_in_mft_record failed with " - "error %i.\n", err < 0 ? err : bw); - } -err_out: - if (ctx) - ntfs_put_attr_search_ctx(ctx); - if (uname && (uname != AT_UNNAMED)) - free(uname); - if (rl) - free(rl); - return err; -} - -/* Return 0 on success and -errno on error. */ -int insert_resident_attr_in_mft_record(MFT_RECORD *m, const ATTR_TYPES type, - const char *name, u32 name_len, const IGNORE_CASE_BOOL ic, - const ATTR_FLAGS flags, const RESIDENT_ATTR_FLAGS res_flags, - const char *val, const u32 val_len) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - int asize, err, i; - uchar_t *uname; -/* - if (base record) - lookup_attr(); - else -*/ - if (name_len) { - i = (name_len + 1) * sizeof(uchar_t); - uname = (uchar_t*)calloc(1, i); - name_len = stoucs(uname, name, i); - if (name_len > 0xff) - return -ENAMETOOLONG; - } else - uname = AT_UNNAMED; - /* Check if the attribute is already there. */ - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) { - Eprintf("Failed to allocate attribute search context.\n"); - err = -ENOMEM; - goto err_out; - } - if (ic == IGNORE_CASE) { - Eprintf("FIXME: Hit unimplemented code path #3.\n"); - err = -ENOTSUP; - goto err_out; - } - if (!ntfs_lookup_attr(type, uname, name_len, ic, 0, val, val_len, - ctx)) { - err = -EEXIST; - goto err_out; - } - if (errno != ENOENT) { - Eprintf("Corrupt inode.\n"); - err = -errno; - goto err_out; - } - a = ctx->attr; - /* sizeof(resident attribute record header) == 24 */ - asize = ((24 + ((name_len + 7) & ~7) + val_len) + 7) & ~7; - err = make_room_for_attribute(m, (char*)a, asize); - if (err == -ENOSPC) { - // FIXME: Make space! (AIA) - // can we make it non-resident? if yes, do that. - // does it fit now? yes -> do it. - // m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? - // yes -> make non-resident - // does it fit now? yes -> do it. - // make all attributes non-resident - // does it fit now? yes -> do it. - // m is a base record? yes -> allocate extension record - // does the new attribute fit in there? yes -> do it. - // split up run_list into extents and place each in an extension - // record. - // FIXME: the check for needing extension records should be - // earlier on as it is very quick: asize > m->bytes_allocated? - err = -ENOTSUP; - goto err_out; - } -#ifdef DEBUG - if (err == -EINVAL) { - fprintf(stderr, "BUG(): in insert_resident_attribute_in_mft_" - "record(): make_room_for_attribute() returned " - "error: EINVAL!\n"); - goto err_out; - } -#endif - a->type = type; - a->length = cpu_to_le32(asize); - a->non_resident = 0; - a->name_length = name_len; - a->name_offset = cpu_to_le16(24); - a->flags = cpu_to_le16(flags); - a->instance = m->next_attr_instance; - m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) - + 1) & 0xffff); - a->value_length = cpu_to_le32(val_len); - a->value_offset = cpu_to_le16(24 + ((name_len + 7) & ~7)); - a->resident_flags = res_flags; - a->reservedR = 0; - if (name_len) - memcpy((char*)a + 24, uname, name_len << 1); - if (val_len) - memcpy((char*)a + le16_to_cpu(a->value_offset), val, val_len); -err_out: - if (ctx) - ntfs_put_attr_search_ctx(ctx); - if (uname && (uname != AT_UNNAMED)) - free(uname); - return err; -} - -s64 time2ntfs(s64 time) -{ - return cpu_to_le64((time + (s64)(369 * 365 + 89) * 24 * 3600) - * 10000000); -} - -/* Return 0 on success or -errno on error. */ -int add_attr_std_info(MFT_RECORD *m, const FILE_ATTR_FLAGS flags) -{ - STANDARD_INFORMATION si; - int err; - - si.creation_time = time2ntfs(time(NULL)); - si.last_data_change_time = si.creation_time; - si.last_mft_change_time = si.creation_time; - si.last_access_time = si.creation_time; - si.file_attributes = flags; /* already LE */ - if (vol->major_ver < 3) - memset(&si.reserved12, 0, sizeof(si.reserved12)); - else { - si.maximum_versions = cpu_to_le32(0); - si.version_number = cpu_to_le32(0); - si.class_id = cpu_to_le32(0); - /* FIXME: $Secure support... */ - si.security_id = cpu_to_le32(0); - /* FIXME: $Quota support... */ - si.owner_id = cpu_to_le32(0); - si.quota_charged = cpu_to_le64(0ULL); - /* FIXME: $UsnJrnl support... */ - si.usn = cpu_to_le64(0ULL); - } - /* NTFS 1.2: size of si = 48, NTFS 3.0: size of si = 72 */ - err = insert_resident_attr_in_mft_record(m, AT_STANDARD_INFORMATION, - NULL, 0, 0, 0, 0, (char*)&si, - vol->major_ver < 3 ? 48 : 72); - if (err < 0) - Eprintf("add_attr_std_info failed: %s\n", strerror(-err)); - return err; -} - -/* Return 0 on success or -errno on error. */ -int add_attr_file_name(MFT_RECORD *m, const MFT_REF parent_dir, - const s64 allocated_size, const s64 data_size, - const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, - const u32 reparse_point_tag, const char *file_name, - const FILE_NAME_TYPE_FLAGS file_name_type) -{ - ntfs_attr_search_ctx *ctx; - STANDARD_INFORMATION *si; - FILE_NAME_ATTR *fn; - int i, fn_size; - - /* Check if the attribute is already there. */ - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) { - Eprintf("Failed to allocate attribute search context.\n"); - return -ENOMEM; - } - if (ntfs_lookup_attr(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx)) { - int eo = errno; - Eprintf("BUG: Standard information attribute not present in " - "file record\n"); - ntfs_put_attr_search_ctx(ctx); - return -eo; - } - si = (STANDARD_INFORMATION*)((char*)ctx->attr + - le16_to_cpu(ctx->attr->value_offset)); - i = (strlen(file_name) + 1) * sizeof(uchar_t); - fn_size = sizeof(FILE_NAME_ATTR) + i; - fn = (FILE_NAME_ATTR*)malloc(fn_size); - if (!fn) { - ntfs_put_attr_search_ctx(ctx); - return -errno; - } - fn->parent_directory = parent_dir; - - fn->creation_time = si->creation_time; - fn->last_data_change_time = si->last_data_change_time; - fn->last_mft_change_time = si->last_mft_change_time; - fn->last_access_time = si->last_access_time; - ntfs_put_attr_search_ctx(ctx); - - fn->allocated_size = cpu_to_le64(allocated_size); - fn->data_size = cpu_to_le64(data_size); - fn->file_attributes = flags; - /* These are in a union so can't have both. */ - if (packed_ea_size && reparse_point_tag) { - free(fn); - return -EINVAL; - } - if (packed_ea_size) { - fn->packed_ea_size = cpu_to_le16(packed_ea_size); - fn->reserved = cpu_to_le16(0); - } else - fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); - fn->file_name_type = file_name_type; - i = stoucs(fn->file_name, file_name, i); - if (i < 1) { - free(fn); - return -EINVAL; - } - if (i > 0xff) { - free(fn); - return -ENAMETOOLONG; - } - /* No terminating null in file names. */ - fn->file_name_length = i; - fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(uchar_t); - i = insert_resident_attr_in_mft_record(m, AT_FILE_NAME, NULL, 0, 0, - 0, RESIDENT_ATTR_IS_INDEXED, (char*)fn, fn_size); - free(fn); - if (i < 0) - Eprintf("add_attr_file_name failed: %s\n", strerror(-i)); - return i; -} - -/* - * Create the security descriptor attribute adding the security descriptor @sd - * of length @sd_len to the mft record @m. - * - * Return 0 on success or -errno on error. - */ -int add_attr_sd(MFT_RECORD *m, const char *sd, const s64 sd_len) -{ - int err; - - /* Does it fit? NO: create non-resident. YES: create resident. */ - if (le32_to_cpu(m->bytes_in_use) + 24 + sd_len > - le32_to_cpu(m->bytes_allocated)) - err = insert_non_resident_attr_in_mft_record(m, - AT_SECURITY_DESCRIPTOR, NULL, 0, 0, 0, sd, - sd_len); - else - err = insert_resident_attr_in_mft_record(m, - AT_SECURITY_DESCRIPTOR, NULL, 0, 0, 0, 0, sd, - sd_len); - if (err < 0) - Eprintf("add_attr_sd failed: %s\n", strerror(-err)); - return err; -} - -/* Return 0 on success or -errno on error. */ -int add_attr_data(MFT_RECORD *m, const char *name, const u32 name_len, - const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, - const char *val, const s64 val_len) -{ - int err; - - /* - * Does it fit? NO: create non-resident. YES: create resident. - * - * FIXME: Introduced arbitrary limit of mft record allocated size - 512. - * This is to get around the problem that if $Bitmap/$DATA becomes too - * big, but is just small enough to be resident, we would make it - * resident, and later run out of space when creating the other - * attributes and this would cause us to abort as making resident - * attributes non-resident is not supported yet. - * The proper fix is to support making resident attribute non-resident. - */ - if (le32_to_cpu(m->bytes_in_use) + 24 + val_len > - min(le32_to_cpu(m->bytes_allocated), - le32_to_cpu(m->bytes_allocated) - 512)) - err = insert_non_resident_attr_in_mft_record(m, AT_DATA, name, - name_len, ic, flags, val, val_len); - else - err = insert_resident_attr_in_mft_record(m, AT_DATA, name, - name_len, ic, flags, 0, val, val_len); - - if (err < 0) - Eprintf("add_attr_data failed: %s\n", strerror(-err)); - return err; -} - -/* - * Create a non-resident data attribute with a predefined on disk location - * specified by the run_list @rl. The clusters specified by @rl are assumed to - * be allocated already. - * - * Return 0 on success or -errno on error. - */ -int add_attr_data_positioned(MFT_RECORD *m, const char *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const ATTR_FLAGS flags, const run_list *rl, - const char *val, const s64 val_len) -{ - int err; - - err = insert_positioned_attr_in_mft_record(m, AT_DATA, name, name_len, - ic, flags, rl, val, val_len); - if (err < 0) - Eprintf("add_attr_data_positioned failed: %s\n", - strerror(-err)); - return err; -} - -/* - * Create volume name attribute specifying the volume name @vol_name as a null - * terminated char string of length @vol_name_len (number of characters not - * including the terminating null), which is converted internally to a little - * endian uchar_t string. The name is at least 1 character long and at most - * 0xff characters long (not counting the terminating null). - * - * Return 0 on success or -errno on error. - */ -int add_attr_vol_name(MFT_RECORD *m, const char *vol_name, - const int vol_name_len) -{ - uchar_t *uname; - int i, len; - - if (vol_name_len) { - len = (vol_name_len + 1) * sizeof(uchar_t); - uname = calloc(1, len); - if (!uname) - return -errno; - i = (stoucs(uname, vol_name, len) + 1) * sizeof(uchar_t); - if (!i) { - free(uname); - return -EINVAL; - } - if (i > 0xff) { - free(uname); - return -ENAMETOOLONG; - } - } else { - uname = NULL; - len = 0; - } - i = insert_resident_attr_in_mft_record(m, AT_VOLUME_NAME, NULL, 0, 0, - 0, 0, (char*)uname, len); - if (uname) - free(uname); - if (i < 0) - Eprintf("add_attr_vol_name failed: %s\n", strerror(-i)); - return i; -} - -/* Return 0 on success or -errno on error. */ -int add_attr_vol_info(MFT_RECORD *m, const VOLUME_FLAGS flags, - const u8 major_ver, const u8 minor_ver) -{ - VOLUME_INFORMATION vi; - int err; - - memset(&vi, 0, sizeof(vi)); - vi.major_ver = major_ver; - vi.minor_ver = minor_ver; - vi.flags = flags & VOLUME_FLAGS_MASK; - err = insert_resident_attr_in_mft_record(m, AT_VOLUME_INFORMATION, NULL, - 0, 0, 0, 0, (char*)&vi, sizeof(vi)); - if (err < 0) - Eprintf("add_attr_vol_info failed: %s\n", strerror(-err)); - return err; -} - -/* Return 0 on success or -errno on error. */ -int add_attr_index_root(MFT_RECORD *m, const char *name, const u32 name_len, - const IGNORE_CASE_BOOL ic, const ATTR_TYPES indexed_attr_type, - const COLLATION_RULES collation_rule, - const u32 index_block_size) -{ - INDEX_ROOT *r; - INDEX_ENTRY_HEADER *e; - int err, val_len; - - val_len = sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER); - r = (INDEX_ROOT*)malloc(val_len); - if (!r) - return -errno; - r->type = indexed_attr_type == AT_FILE_NAME ? AT_FILE_NAME : 0; - if (indexed_attr_type == AT_FILE_NAME && - collation_rule != COLLATION_FILE_NAME) { - free(r); - Eprintf("add_attr_index_root: indexed attribute is $FILE_NAME " - "but collation rule is not COLLATION_FILE_NAME.\n"); - return -EINVAL; - } - r->collation_rule = collation_rule; - r->index_block_size = cpu_to_le32(index_block_size); - if (index_block_size >= vol->cluster_size) { - if (index_block_size % vol->cluster_size) { - Eprintf("add_attr_index_root: index block size is not " - "a multiple of the cluster size.\n"); - free(r); - return -EINVAL; - } - r->clusters_per_index_block = index_block_size / - vol->cluster_size; - } else /* if (vol->cluster_size > index_block_size) */ { - if (index_block_size & (index_block_size - 1)) { - Eprintf("add_attr_index_root: index block size is not " - "a power of 2.\n"); - free(r); - return -EINVAL; - } - if (index_block_size < opt.sector_size) { - Eprintf("add_attr_index_root: index block size is " - "smaller than the sector size.\n"); - free(r); - return -EINVAL; - } - r->clusters_per_index_block = index_block_size / - opt.sector_size; - } - memset(&r->reserved, 0, sizeof(r->reserved)); - r->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); - r->index.index_length = cpu_to_le32(sizeof(INDEX_HEADER) + - sizeof(INDEX_ENTRY_HEADER)); - r->index.allocated_size = r->index.index_length; - r->index.flags = SMALL_INDEX; - memset(&r->index.reserved, 0, sizeof(r->index.reserved)); - e = (INDEX_ENTRY_HEADER*)((char*)&r->index + - le32_to_cpu(r->index.entries_offset)); - /* - * No matter whether this is a file index or a view as this is a - * termination entry, hence no key value / data is associated with it - * at all. Thus, we just need the union to be all zero. - */ - e->indexed_file = cpu_to_le64(0LL); - e->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); - e->key_length = cpu_to_le16(0); - e->flags = INDEX_ENTRY_END; - e->reserved = cpu_to_le16(0); - err = insert_resident_attr_in_mft_record(m, AT_INDEX_ROOT, name, - name_len, ic, 0, 0, (char*)r, val_len); - free(r); - if (err < 0) - Eprintf("add_attr_index_root failed: %s\n", strerror(-err)); - return err; -} - -/* Return 0 on success or -errno on error. */ -int add_attr_index_alloc(MFT_RECORD *m, const char *name, const u32 name_len, - const IGNORE_CASE_BOOL ic, const char *index_alloc_val, - const u32 index_alloc_val_len) -{ - int err; - - err = insert_non_resident_attr_in_mft_record(m, AT_INDEX_ALLOCATION, - name, name_len, ic, 0, index_alloc_val, - index_alloc_val_len); - if (err < 0) - Eprintf("add_attr_index_alloc failed: %s\n", strerror(-err)); - return err; -} - -/* Return 0 on success or -errno on error. */ -int add_attr_bitmap(MFT_RECORD *m, const char *name, const u32 name_len, - const IGNORE_CASE_BOOL ic, const char *bitmap, - const u32 bitmap_len) -{ - int err; - - /* Does it fit? NO: create non-resident. YES: create resident. */ - if (le32_to_cpu(m->bytes_in_use) + 24 + bitmap_len > - le32_to_cpu(m->bytes_allocated)) - err = insert_non_resident_attr_in_mft_record(m, AT_BITMAP, name, - name_len, ic, 0, bitmap, bitmap_len); - else - err = insert_resident_attr_in_mft_record(m, AT_BITMAP, name, - name_len, ic, 0, 0, bitmap, bitmap_len); - - if (err < 0) - Eprintf("add_attr_bitmap failed: %s\n", strerror(-err)); - return err; -} - -/* - * Create a non-resident bitmap attribute with a predefined on disk location - * specified by the run_list @rl. The clusters specified by @rl are assumed to - * be allocated already. - * - * Return 0 on success or -errno on error. - */ -int add_attr_bitmap_positioned(MFT_RECORD *m, const char *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const run_list *rl, const char *bitmap, const u32 bitmap_len) -{ - int err; - - err = insert_positioned_attr_in_mft_record(m, AT_BITMAP, name, name_len, - ic, 0, rl, bitmap, bitmap_len); - if (err < 0) - Eprintf("add_attr_bitmap_positioned failed: %s\n", - strerror(-err)); - return err; -} - -/* - * Create bitmap and index allocation attributes, modify index root - * attribute accordingly and move all of the index entries from the index root - * into the index allocation. - * - * Return 0 on success or -errno on error. - */ -int upgrade_to_large_index(MFT_RECORD *m, const char *name, - u32 name_len, const IGNORE_CASE_BOOL ic, - INDEX_ALLOCATION **index) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - INDEX_ROOT *r; - INDEX_ENTRY *re; - INDEX_ALLOCATION *ia_val = NULL; - uchar_t *uname; - char bmp[8]; - char *re_start, *re_end; - int i, err, index_block_size; - - if (name_len) { - i = (name_len + 1) * sizeof(uchar_t); - uname = (uchar_t*)calloc(1, i); - if (!uname) - return -errno; - name_len = stoucs(uname, name, i); - if (name_len > 0xff) { - free(uname); - return -ENAMETOOLONG; - } - } else - uname = NULL; - /* Find the index root attribute. */ - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) { - Eprintf("Failed to allocate attribute search context.\n"); - return -ENOMEM; - } - if (ic == IGNORE_CASE) { - Eprintf("FIXME: Hit unimplemented code path #4.\n"); - err = -ENOTSUP; - goto err_out; - } - err = ntfs_lookup_attr(AT_INDEX_ROOT, uname, name_len, ic, 0, NULL, 0, - ctx); - if (uname) - free(uname); - if (err) { - err = -ENOTDIR; - goto err_out; - } - a = ctx->attr; - if (a->non_resident || a->flags) { - err = -EINVAL; - goto err_out; - } - r = (INDEX_ROOT*)((char*)a + le16_to_cpu(a->value_offset)); - re_end = (char*)r + le32_to_cpu(a->value_length); - re_start = (char*)&r->index + le32_to_cpu(r->index.entries_offset); - re = (INDEX_ENTRY*)re_start; - index_block_size = le32_to_cpu(r->index_block_size); - memset(bmp, 0, sizeof(bmp)); - ntfs_set_bit(bmp, 0ULL, 1); - /* Bitmap has to be at least 8 bytes in size. */ - err = add_attr_bitmap(m, name, name_len, ic, (char*)&bmp, sizeof(bmp)); - if (err) - goto err_out; - ia_val = calloc(1, index_block_size); - if (!ia_val) { - err = -errno; - goto err_out; - } - /* Setup header. */ - ia_val->magic = magic_INDX; - ia_val->usa_ofs = cpu_to_le16(sizeof(INDEX_ALLOCATION)); - if (index_block_size >= NTFS_SECTOR_SIZE) - ia_val->usa_count = cpu_to_le16(index_block_size / - NTFS_SECTOR_SIZE + 1); - else { - ia_val->usa_count = cpu_to_le16(1); - Qprintf("Sector size is bigger than index block size. Setting " - "usa_count to 1. If Windows\nchkdsk reports this as " - "corruption, please email linux-ntfs-dev@lists.sf.net\n" - "stating that you saw this message and that the file " - "system created was corrupt.\nThank you."); - } - /* Set USN to 1. */ - *(u16*)((char*)ia_val + le16_to_cpu(ia_val->usa_ofs)) = - cpu_to_le16(1); - ia_val->lsn = cpu_to_le64(0); - ia_val->index_block_vcn = cpu_to_le64(0); - ia_val->index.flags = LEAF_NODE; - /* Align to 8-byte boundary. */ - ia_val->index.entries_offset = cpu_to_le32((sizeof(INDEX_HEADER) + - le16_to_cpu(ia_val->usa_count) * 2 + 7) & ~7); - ia_val->index.allocated_size = cpu_to_le32(index_block_size - - (sizeof(INDEX_ALLOCATION) - sizeof(INDEX_HEADER))); - /* Find the last entry in the index root and save it in re. */ - while ((char*)re < re_end && !(re->flags & INDEX_ENTRY_END)) { - /* Next entry in index root. */ - re = (INDEX_ENTRY*)((char*)re + le16_to_cpu(re->length)); - } - /* Copy all the entries including the termination entry. */ - i = (char*)re - re_start + le16_to_cpu(re->length); - memcpy((char*)&ia_val->index + - le32_to_cpu(ia_val->index.entries_offset), re_start, i); - /* Finish setting up index allocation. */ - ia_val->index.index_length = cpu_to_le32(i + - le32_to_cpu(ia_val->index.entries_offset)); - /* Move the termination entry forward to the beginning if necessary. */ - if ((char*)re > re_start) { - memmove(re_start, (char*)re, le16_to_cpu(re->length)); - re = (INDEX_ENTRY*)re_start; - } - /* Now fixup empty index root with pointer to index allocation VCN 0. */ - r->index.flags = LARGE_INDEX; - re->flags |= INDEX_ENTRY_NODE; - if (le16_to_cpu(re->length) < sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)) - re->length = cpu_to_le16(le16_to_cpu(re->length) + sizeof(VCN)); - r->index.index_length = cpu_to_le32(le32_to_cpu(r->index.entries_offset) - + le16_to_cpu(re->length)); - r->index.allocated_size = r->index.index_length; - /* Resize index root attribute. */ - err = resize_resident_attribute_value(m, a, sizeof(INDEX_ROOT) - - sizeof(INDEX_HEADER) + - le32_to_cpu(r->index.allocated_size)); - if (err) { - // TODO: Remove the added bitmap! - // Revert index root from index allocation. - goto err_out; - } - /* Set VCN pointer to 0LL. */ - *(VCN*)((char*)re + cpu_to_le16(re->length) - sizeof(VCN)) = - cpu_to_le64(0); - err = ntfs_pre_write_mst_fixup((NTFS_RECORD*)ia_val, index_block_size); - if (err) { - err = -errno; - Eprintf("ntfs_pre_write_mst_fixup() failed in " - "upgrade_to_large_index.\n"); - goto err_out; - } - err = add_attr_index_alloc(m, name, name_len, ic, (char*)ia_val, - index_block_size); - ntfs_post_write_mst_fixup((NTFS_RECORD*)ia_val); - if (err) { - // TODO: Remove the added bitmap! - // Revert index root from index allocation. - goto err_out; - } - *index = ia_val; - return 0; -err_out: - if (ctx) - ntfs_put_attr_search_ctx(ctx); - if (ia_val) - free(ia_val); - return err; -} - -/* - * Create space of @size bytes at position @pos inside the index block @index. - * - * Return 0 on success or -errno on error. - */ -int make_room_for_index_entry_in_index_block(INDEX_BLOCK *index, - INDEX_ENTRY *pos, u32 size) -{ - u32 biu; - - if (!size) - return 0; -#ifdef DEBUG - /* - * Rigorous consistency checks. Always return -EINVAL even if more - * appropriate codes exist for simplicity of parsing the return value. - */ - if (size != ((size + 7) & ~7)) { - Eprintf("make_room_for_index_entry_in_index_block() received " - "non 8-byte aligned size.\n"); - return -EINVAL; - } - if (!index || !pos) - return -EINVAL; - if ((char*)pos < (char*)index || (char*)pos + size < (char*)index || - (char*)pos > (char*)index + sizeof(INDEX_BLOCK) - - sizeof(INDEX_HEADER) + - le32_to_cpu(index->index.allocated_size) || - (char*)pos + size > (char*)index + sizeof(INDEX_BLOCK) - - sizeof(INDEX_HEADER) + - le32_to_cpu(index->index.allocated_size)) - return -EINVAL; - /* The - sizeof(INDEX_ENTRY_HEADER) is for the index terminator. */ - if ((char*)pos - (char*)&index->index > - le32_to_cpu(index->index.index_length) - - sizeof(INDEX_ENTRY_HEADER)) - return -EINVAL; -#endif - biu = le32_to_cpu(index->index.index_length); - /* Do we have enough space? */ - if (biu + size > le32_to_cpu(index->index.allocated_size)) - return -ENOSPC; - /* Move everything after pos to pos + size. */ - memmove((char*)pos + size, (char*)pos, biu - ((char*)pos - - (char*)&index->index)); - /* Update index block. */ - index->index.index_length = cpu_to_le32(biu + size); - return 0; -} - -/* - * Insert the fully completed FILE_NAME_ATTR @file_name which is inside - * the file with mft reference @file_ref into the index (allocation) block - * @index (which belongs to @file_ref's parent directory). - * - * Return 0 on success or -errno on error. - */ -int insert_file_link_in_dir_index(INDEX_BLOCK *index, MFT_REF file_ref, - FILE_NAME_ATTR *file_name, u32 file_name_size) -{ - int err, i; - INDEX_ENTRY *ie; - char *index_end; - - /* - * Lookup dir entry @file_name in dir @index to determine correct - * insertion location. FIXME: Using a very oversimplified lookup - * method which is sufficient for mkntfs but no good whatsoever in - * real world scenario. (AIA) - */ - index_end = (char*)&index->index + - le32_to_cpu(index->index.index_length); - ie = (INDEX_ENTRY*)((char*)&index->index + - le32_to_cpu(index->index.entries_offset)); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - while ((char*)ie < index_end && !(ie->flags & INDEX_ENTRY_END)) { -/* -#ifdef DEBUG - Dprintf("file_name_attr1->file_name_length = %i\n", - file_name->file_name_length); - if (file_name->file_name_length) { - char *__buf; - __buf = (char*)calloc(1, file_name->file_name_length + - 1); - if (!__buf) - err_exit("Failed to allocate internal buffer: " - "%s\n", strerror(errno)); - i = ucstos(__buf, (uchar_t*)&file_name->file_name, - file_name->file_name_length + 1); - if (i == -1) - Dprintf("Name contains non-displayable " - "Unicode characters.\n"); - Dprintf("file_name_attr1->file_name = %s\n", __buf); - free(__buf); - } - Dprintf("file_name_attr2->file_name_length = %i\n", - ie->key.file_name.file_name_length); - if (ie->key.file_name.file_name_length) { - char *__buf; - __buf = (char*)calloc(1, - ie->key.file_name.file_name_length + 1); - if (!__buf) - err_exit("Failed to allocate internal buffer: " - "%s\n", strerror(errno)); - i = ucstos(__buf, ie->key.file_name.file_name, - ie->key.file_name.file_name_length + 1); - if (i == -1) - Dprintf("Name contains non-displayable " - "Unicode characters.\n"); - Dprintf("file_name_attr2->file_name = %s\n", __buf); - free(__buf); - } -#endif -*/ - i = ntfs_file_compare_values(file_name, - (FILE_NAME_ATTR*)&ie->key.file_name, 1, - IGNORE_CASE, vol->upcase, vol->upcase_len); - /* - * If @file_name collates before ie->key.file_name, there is no - * matching index entry. - */ - if (i == -1) - break; - /* If file names are not equal, continue search. */ - if (i) - goto do_next; - /* File names are equal when compared ignoring case. */ - /* - * If BOTH file names are in the POSIX namespace, do a case - * sensitive comparison as well. Otherwise the names match so - * we return -EEXIST. FIXME: There are problems with this in a - * real world scenario, when one is POSIX and one isn't, but - * fine for mkntfs where we don't use POSIX namespace at all - * and hence this following code is luxury. (AIA) - */ - if (file_name->file_name_type != FILE_NAME_POSIX || - ie->key.file_name.file_name_type != FILE_NAME_POSIX) - return -EEXIST; - i = ntfs_file_compare_values(file_name, - (FILE_NAME_ATTR*)&ie->key.file_name, 1, - CASE_SENSITIVE, vol->upcase, vol->upcase_len); - if (i == -1) - break; - /* Complete match. Bugger. Can't insert. */ - if (!i) - return -EEXIST; -do_next: -#ifdef DEBUG - /* Next entry. */ - if (!ie->length) { - Dprintf("BUG: ie->length is zero, breaking out of " - "loop.\n"); - break; - } -#endif - ie = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); - }; - i = (sizeof(INDEX_ENTRY_HEADER) + file_name_size + 7) & ~7; - err = make_room_for_index_entry_in_index_block(index, ie, i); - if (err) { - Eprintf("make_room_for_index_entry_in_index_block failed: " - "%s\n", strerror(-err)); - return err; - } - /* Create entry in place and copy file name attribute value. */ - ie->indexed_file = file_ref; - ie->length = cpu_to_le16(i); - ie->key_length = cpu_to_le16(file_name_size); - ie->flags = cpu_to_le16(0); - ie->reserved = cpu_to_le16(0); - memcpy((char*)&ie->key.file_name, (char*)file_name, file_name_size); - return 0; -} - -/* - * Create a file_name_attribute in the mft record @m_file which points to the - * parent directory with mft reference @ref_parent. - * - * Then, insert an index entry with this file_name_attribute in the index - * block @index of the index allocation attribute of the parent directory. - * - * @ref_file is the mft reference of @m_file. - * - * Return 0 on success or -errno on error. - */ -int create_hardlink(INDEX_BLOCK *index, const MFT_REF ref_parent, - MFT_RECORD *m_file, const MFT_REF ref_file, - const s64 allocated_size, const s64 data_size, - const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, - const u32 reparse_point_tag, const char *file_name, - const FILE_NAME_TYPE_FLAGS file_name_type) -{ - FILE_NAME_ATTR *fn; - int i, fn_size; - - /* Create the file_name attribute. */ - i = (strlen(file_name) + 1) * sizeof(uchar_t); - fn_size = sizeof(FILE_NAME_ATTR) + i; - fn = (FILE_NAME_ATTR*)malloc(fn_size); - if (!fn) - return -errno; - fn->parent_directory = ref_parent; - // FIXME: Is this correct? Or do we have to copy the creation_time - // from the std info? - fn->creation_time = time2ntfs(time(NULL)); - fn->last_data_change_time = fn->creation_time; - fn->last_mft_change_time = fn->creation_time; - fn->last_access_time = fn->creation_time; - fn->allocated_size = cpu_to_le64(allocated_size); - fn->data_size = cpu_to_le64(data_size); - fn->file_attributes = flags; - /* These are in a union so can't have both. */ - if (packed_ea_size && reparse_point_tag) { - free(fn); - return -EINVAL; - } - if (packed_ea_size) { - fn->packed_ea_size = cpu_to_le16(packed_ea_size); - fn->reserved = cpu_to_le16(0); - } else - fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); - fn->file_name_type = file_name_type; - i = stoucs(fn->file_name, file_name, i); - if (i < 1) { - free(fn); - return -EINVAL; - } - if (i > 0xff) { - free(fn); - return -ENAMETOOLONG; - } - /* No terminating null in file names. */ - fn->file_name_length = i; - fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(uchar_t); - /* Increment the link count of @m_file. */ - i = le16_to_cpu(m_file->link_count); - if (i == 0xffff) { - Eprintf("Too many hardlinks present already.\n"); - free(fn); - return -EINVAL; - } - m_file->link_count = cpu_to_le16(i + 1); - /* Add the file_name to @m_file. */ - i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, 0, - 0, RESIDENT_ATTR_IS_INDEXED, (char*)fn, fn_size); - if (i < 0) { - Eprintf("create_hardlink failed adding file name attribute: " - "%s\n", strerror(-i)); - free(fn); - /* Undo link count increment. */ - m_file->link_count = cpu_to_le16( - le16_to_cpu(m_file->link_count) - 1); - return i; - } - /* Insert the index entry for file_name in @index. */ - i = insert_file_link_in_dir_index(index, ref_file, fn, fn_size); - if (i < 0) { - Eprintf("create_hardlink failed inserting index entry: %s\n", - strerror(-i)); - /* FIXME: Remove the file name attribute from @m_file. */ - free(fn); - /* Undo link count increment. */ - m_file->link_count = cpu_to_le16( - le16_to_cpu(m_file->link_count) - 1); - return i; - } - free(fn); - return 0; -} - -void init_options() -{ - memset(&opt, 0, sizeof(opt)); - opt.index_block_size = 4096; - opt.attr_defs = (ATTR_DEF*)&attrdef_ntfs12_array; - opt.attr_defs_len = sizeof(attrdef_ntfs12_array); - //Dprintf("Attr_defs table length = %u\n", opt.attr_defs_len); -} - -void usage(void) __attribute__ ((noreturn)); - -void usage(void) -{ - fprintf(stderr, "Copyright (c) 2001,2002 Anton Altaparmakov.\n" - "Create an NTFS volume on a user specified (block) device.\n" - "Usage: %s [-s sector-size] [-c cluster-size] " - "[-L volume-label]\n\t[-z mft-zone-multiplier] " - "[-fnqvvCFIQV] device [number-of-sectors]\n", - EXEC_NAME); - exit(1); -} - -void parse_options(int argc, char *argv[]) -{ - int c; - long l; - unsigned long u; - char *s; - -// Need to have: mft record size, index record size, ntfs version, mft size, -// logfile size, list of bad blocks, check for bad blocks, ... - if (argc && *argv) - EXEC_NAME = *argv; - fprintf(stderr, "%s v%s\n", EXEC_NAME, VERSION); - while ((c = getopt(argc, argv, "c:fnqs:vz:CFIL:QV")) != EOF) - switch (c) { - case 'n': - opt.no_action = 1; - break; - case 'c': - l = strtol(optarg, &s, 0); - if (!l || l > INT_MAX || *s) - err_exit("Invalid cluster size.\n"); - vol->cluster_size = l; - break; - case 'f': - case 'Q': - opt.quick_format = 1; - break; - case 'q': - opt.quiet = 1; - break; - case 's': - l = strtol(optarg, &s, 0); - if (!l || l > INT_MAX || *s) - err_exit("Invalid sector size.\n"); - opt.sector_size = l; - break; - case 'v': - opt.verbose++; - break; - case 'z': - l = strtol(optarg, &s, 0); - if (l < 1 || l > 4 || *s) - err_exit("Invalid MFT zone multiplier.\n"); - opt.mft_zone_multiplier = l; - break; - case 'C': - opt.enable_compression = 1; - break; - case 'F': - opt.force = 1; - break; - case 'I': - opt.disable_indexing = 1; - break; - case 'L': - vol->vol_name = optarg; - break; - case 'V': - /* Version number already printed, so just exit. */ - exit(0); - default: - usage(); - } - if (optind == argc) - usage(); - vol->dev_name = argv[optind++]; - if (optind < argc) { - u = strtoul(argv[optind++], &s, 0); - if (*s || !u || (u >= ULONG_MAX && errno == ERANGE)) - err_exit("Invalid number of sectors: %s\n", - argv[optind - 1]); - opt.nr_sectors = u; - } - if (optind < argc) - usage(); -} - -void mkntfs_exit(void) -{ - int err; - - if (index_block) - free(index_block); - if (buf) - free(buf); - if (buf2) - free(buf2); - if (lcn_bitmap) - free(lcn_bitmap); - if (mft_bitmap) - free(mft_bitmap); - if (rl) - free(rl); - if (rl_mft) - free(rl_mft); - if (rl_mft_bmp) - free(rl_mft_bmp); - if (rl_mftmirr) - free(rl_mftmirr); - if (rl_logfile) - free(rl_logfile); - if (rl_boot) - free(rl_boot); - if (rl_bad) - free(rl_bad); - if (rl_index) - free(rl_index); - if (opt.bad_blocks) - free(opt.bad_blocks); - if (opt.attr_defs != (ATTR_DEF*)attrdef_ntfs12_array) - free(opt.attr_defs); - if (vol->upcase) - free(vol->upcase); - flk.l_type = F_UNLCK; - err = fcntl(vol->fd, F_SETLK, &flk); - if (err == -1) - Eprintf("Warning: Could not unlock %s: %s\n", vol->dev_name, - strerror(errno)); - err = close(vol->fd); - if (err == -1) - Eprintf("Warning: Could not close %s: %s\n", vol->dev_name, - strerror(errno)); - if (vol) - free(vol); -} - -#define MAKE_MFT_REF(_ref, _seqno) cpu_to_le64((((u64)(_seqno)) << 48) \ - | ((u64)(_ref))) - -static inline int valid_offset(int f, long long ofs) -{ - char ch; - - if (lseek(f, ofs, SEEK_SET) >= 0 && read(f, &ch, 1) == 1) - return 1; - return 0; -} - -/* - * Returns the number of bs sized blocks in a partition. Adapted from - * e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o. - */ -long long get_device_size(int f, int bs) -{ - long long high, low; -#ifdef BLKGETSIZE - long size; - - if (ioctl(f, BLKGETSIZE, &size) >= 0) { - Dprintf("BLKGETSIZE nr 512 byte blocks = %ld (0x%ld)\n", size, - size); - return (long long)size * 512 / bs; - } -#endif -#ifdef FDGETPRM - { struct floppy_struct this_floppy; - - if (ioctl(f, FDGETPRM, &this_floppy) >= 0) { - Dprintf("FDGETPRM nr 512 byte blocks = %ld (0x%ld)\n", - this_floppy.size, this_floppy.size); - return (long long)this_floppy.size * 512 / bs; - } - } -#endif - /* - * We couldn't figure it out by using a specialized ioctl, - * so do binary search to find the size of the partition. - */ - low = 0LL; - for (high = 1024LL; valid_offset(f, high); high <<= 1) - low = high; - while (low < high - 1LL) { - const long long mid = (low + high) / 2; - - if (valid_offset(f, mid)) - low = mid; - else - high = mid; - } - lseek(f, 0LL, SEEK_SET); - return (low + 1LL) / bs; -} - -int main(int argc, char **argv) -{ - int i, j, err; - ssize_t bw; - struct stat sbuf; - long long lw, pos; - MFT_RECORD *m; - ATTR_RECORD *a; - MFT_REF root_ref; - ntfs_attr_search_ctx *ctx; - char *sd; - NTFS_BOOT_SECTOR *bs; - unsigned long mnt_flags; - - /* Initialize the random number generator with the current time. */ - srandom(time(NULL)); - /* Initialize ntfs_volume structure vol. */ - vol = (ntfs_volume*)calloc(1, sizeof(*vol)); - if (!vol) - err_exit("Could not allocate memory for internal buffer.\n"); - vol->major_ver = 1; - vol->minor_ver = 2; - vol->mft_record_size = 1024; - vol->mft_record_size_bits = 10; - /* Length is in unicode characters. */ - vol->upcase_len = 65536; - vol->upcase = (uchar_t*)malloc(vol->upcase_len * sizeof(uchar_t)); - if (!vol->upcase) - err_exit("Could not allocate memory for internal buffer.\n"); - init_upcase_table(vol->upcase, vol->upcase_len * sizeof(uchar_t)); - /* Initialize opt to zero / required values. */ - init_options(); - /* Parse command line options. */ - parse_options(argc, argv); - /* Verify we are dealing with a block device. */ - if (stat(vol->dev_name, &sbuf) == -1) { - if (errno == ENOENT) - err_exit("The device doesn't exist; did you specify " - "it correctly?\n"); - err_exit("Error getting information about %s: %s\n", - vol->dev_name, strerror(errno)); - } - if (!S_ISBLK(sbuf.st_mode)) { - Eprintf("%s is not a block device.\n", vol->dev_name); - if (!opt.force) - err_exit("Refusing to make a filesystem here!\n"); - if (!opt.nr_sectors) { - if (!sbuf.st_size && !sbuf.st_blocks) - err_exit("You must specify the number of " - "sectors.\n"); - if (opt.sector_size) { - if (sbuf.st_size) - opt.nr_sectors = sbuf.st_size / - opt.sector_size; - else - opt.nr_sectors = ((s64)sbuf.st_blocks - << 9) / opt.sector_size; - } else { - if (sbuf.st_size) - opt.nr_sectors = sbuf.st_size / 512; - else - opt.nr_sectors = sbuf.st_blocks; - opt.sector_size = 512; - } - } - fprintf(stderr, "mkntfs forced anyway.\n"); - } -#ifdef HAVE_LINUX_MAJOR_H - else if ((MAJOR(sbuf.st_rdev) == HD_MAJOR && - MINOR(sbuf.st_rdev) % 64 == 0) || - (SCSI_BLK_MAJOR(MAJOR(sbuf.st_rdev)) && - MINOR(sbuf.st_rdev) % 16 == 0)) { - err_exit("%s is entire device, not just one partition!\n", - vol->dev_name); - } -#endif - /* Make sure the file system is not mounted. */ - if (ntfs_check_if_mounted(vol->dev_name, &mnt_flags)) - Eprintf("Failed to determine whether %s is mounted: %s\n", - vol->dev_name, strerror(errno)); - else if (mnt_flags & NTFS_MF_MOUNTED) { - Eprintf("%s is mounted.\n", vol->dev_name); - if (!opt.force) - err_exit("Refusing to make a filesystem here!\n"); - fprintf(stderr, "mkntfs forced anyway. Hope /etc/mtab is " - "incorrect.\n"); - } - - /* Open the device for reading or reading and writing. */ - if (opt.no_action) { - Qprintf("Running in READ-ONLY mode!\n"); - i = O_RDONLY; - } else - i = O_RDWR; - vol->fd = open(vol->dev_name, i); - if (vol->fd == -1) - err_exit("Could not open %s: %s\n", vol->dev_name, - strerror(errno)); - /* Acquire exlusive (mandatory) write lock on the whole device. */ - memset(&flk, 0, sizeof(flk)); - if (opt.no_action) - flk.l_type = F_RDLCK; - else - flk.l_type = F_WRLCK; - flk.l_whence = SEEK_SET; - flk.l_start = flk.l_len = 0LL; - err = fcntl(vol->fd, F_SETLK, &flk); - if (err == -1) { - Eprintf("Could not lock %s for %s: %s\n", vol->dev_name, - opt.no_action ? "reading" : "writing", - strerror(errno)); - err = close(vol->fd); - if (err == -1) - Eprintf("Warning: Could not close %s: %s\n", - vol->dev_name, strerror(errno)); - exit(1); - } - /* Register our exit function which will unlock and close the device. */ - err = atexit(&mkntfs_exit); - if (err == -1) { - Eprintf("Could not set up exit() function because atexit() " - "failed. Aborting...\n"); - mkntfs_exit(); - exit(1); - } - /* If user didn't specify the sector size, determine it now. */ - if (!opt.sector_size) { -#ifdef BLKSSZGET - int _sect_size = 0; - - if (ioctl(vol->fd, BLKSSZGET, &_sect_size) >= 0) - opt.sector_size = _sect_size; - else -#endif - { - Eprintf("No sector size specified for %s and it could " - "not be obtained automatically.\n" - "Assuming sector size is 512 bytes.\n", - vol->dev_name); - opt.sector_size = 512; - } - } - /* Validate sector size. */ - if ((opt.sector_size - 1) & opt.sector_size || - opt.sector_size < 256 || opt.sector_size > 4096) - err_exit("Error: sector_size is invalid. It must be a power " - "of two, and it must be\n greater or equal 256 and " - "less than or equal 4096 bytes.\n"); - Dprintf("sector size = %i bytes\n", opt.sector_size); - /* If user didn't specify the number of sectors, determine it now. */ - if (!opt.nr_sectors) { - opt.nr_sectors = get_device_size(vol->fd, opt.sector_size); - if (opt.nr_sectors <= 0) - err_exit("get_device_size(%s) failed. Please specify " - "it manually.\n", vol->dev_name); - } - Dprintf("number of sectors = %Ld (0x%Lx)\n", opt.nr_sectors, - opt.nr_sectors); - /* Reserve the last sector for the backup boot sector. */ - opt.nr_sectors--; - /* If user didn't specify the volume size, determine it now. */ - if (!opt.volume_size) - opt.volume_size = opt.nr_sectors * opt.sector_size; - else if (opt.volume_size & (opt.sector_size - 1)) - err_exit("Error: volume_size is not a multiple of " - "sector_size.\n"); - /* Validate volume size. */ - if (opt.volume_size < 1 << 20 /* 1MiB */) - err_exit("Error: device is too small (%ikiB). Minimum NTFS " - "volume size is 1MiB.\n", opt.volume_size / 1024); - Dprintf("volume size = %LikiB\n", opt.volume_size / 1024); - /* If user didn't specify the cluster size, determine it now. */ - if (!vol->cluster_size) { - if (opt.volume_size <= 512LL << 20) /* <= 512MB */ - vol->cluster_size = 512; - else if (opt.volume_size <= 1LL << 30) /* ]512MB-1GB] */ - vol->cluster_size = 1024; - else if (opt.volume_size <= 2LL << 30) /* ]1GB-2GB] */ - vol->cluster_size = 2048; - else - vol->cluster_size = 4096; - /* For small volumes on devices with large sector sizes. */ - if (vol->cluster_size < opt.sector_size) - vol->cluster_size = opt.sector_size; - } - /* Validate cluster size. */ - if (vol->cluster_size & (vol->cluster_size - 1) || - vol->cluster_size < opt.sector_size || - vol->cluster_size > 128 * opt.sector_size || - vol->cluster_size > 65536) - err_exit("Error: cluster_size is invalid. It must be a power " - "of two, be at least\nthe same as sector_size, be " - "maximum 64kB, and the sectors per cluster value " - "has\nto fit inside eight bits. (We do not support " - "larger cluster sizes yet.)\n"); - vol->cluster_size_bits = ffs(vol->cluster_size) - 1; - Dprintf("cluster size = %i bytes\n", vol->cluster_size); - if (vol->cluster_size > 4096) { - if (opt.enable_compression) { - if (!opt.force) - err_exit("Error: cluster_size is above 4096 " - "bytes and compression is " - "requested.\nThis is not " - "possible due to limitations " - "in the compression algorithm " - "used by\nWindows.\n"); - opt.enable_compression = 0; - } - Qprintf("Warning: compression will be disabled on this volume " - "because it is not\nsupported when the cluster " - "size is above 4096 bytes. This is due to \n" - "limitations in the compression algorithm used " - "by Windows.\n"); - } - /* If user didn't specify the number of clusters, determine it now. */ - if (!opt.nr_clusters) - opt.nr_clusters = opt.volume_size / vol->cluster_size; - /* - * Check the cluster_size and nr_sectors for consistency with - * sector_size and nr_sectors. And check both of these for consistency - * with volume_size. - */ - if (opt.nr_clusters != (opt.nr_sectors * opt.sector_size) / - vol->cluster_size || - opt.volume_size / opt.sector_size != opt.nr_sectors || - opt.volume_size / vol->cluster_size != opt.nr_clusters) - err_exit("Illegal combination of volume/cluster/sector size " - "and/or cluster/sector number.\n"); - Dprintf("number of clusters = %Lu (0x%Lx)\n", opt.nr_clusters, - opt.nr_clusters); - /* Determine lcn bitmap byte size and allocate it. */ - lcn_bitmap_byte_size = (opt.nr_clusters + 7) >> 3; - /* Needs to be multiple of 8 bytes. */ - lcn_bitmap_byte_size = (lcn_bitmap_byte_size + 7) & ~7; - i = (lcn_bitmap_byte_size + vol->cluster_size - 1) & - ~(vol->cluster_size - 1); - Dprintf("lcn_bitmap_byte_size = %i, allocated = %i\n", - lcn_bitmap_byte_size, i); - lcn_bitmap = (unsigned char *)calloc(1, lcn_bitmap_byte_size); - if (!lcn_bitmap) - err_exit("Failed to allocate internal buffer: %s", - strerror(errno)); - /* - * $Bitmap can overlap the end of the volume. Any bits in this region - * must be set. This region also encompasses the backup boot sector. - */ - for (i = opt.nr_clusters; i < lcn_bitmap_byte_size << 3; i++) - ntfs_set_bit(lcn_bitmap, (u64)i, 1); - /* - * Determine mft_size: 16 mft records or 1 cluster, which ever is - * bigger, rounded to multiples of cluster size. - */ - opt.mft_size = (16 * vol->mft_record_size + vol->cluster_size - 1) - & ~(vol->cluster_size - 1); - Dprintf("MFT size = %i (0x%x) bytes\n", opt.mft_size, opt.mft_size); - /* Determine mft bitmap size and allocate it. */ - mft_bitmap_size = opt.mft_size / vol->mft_record_size; - /* Convert to bytes, at least one. */ - mft_bitmap_byte_size = (mft_bitmap_size + 7) >> 3; - /* Mft bitmap is allocated in multiples of 8 bytes. */ - mft_bitmap_byte_size = (mft_bitmap_byte_size + 7) & ~7; - Dprintf("mft_bitmap_size = %i, mft_bitmap_byte_size = %i\n", - mft_bitmap_size, mft_bitmap_byte_size); - mft_bitmap = (unsigned char *)calloc(1, mft_bitmap_byte_size); - if (!mft_bitmap) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - /* Create run list for mft bitmap. */ - rl_mft_bmp = (run_list *)malloc(2 * sizeof(run_list)); - if (!rl_mft_bmp) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - rl_mft_bmp[0].vcn = 0LL; - /* Mft bitmap is right after $Boot's data. */ - j = (8192 + vol->cluster_size - 1) / vol->cluster_size; - rl_mft_bmp[0].lcn = j; - /* - * Size is always one cluster, even though valid data size and - * initialized data size are only 8 bytes. - */ - rl_mft_bmp[1].vcn = rl_mft_bmp[0].length = 1LL; - rl_mft_bmp[1].lcn = -1LL; - rl_mft_bmp[1].length = 0LL; - /* Allocate cluster for mft bitmap. */ - ntfs_set_bit(lcn_bitmap, (s64)j, 1); - /* If user didn't specify the mft lcn, determine it now. */ - if (!opt.mft_lcn) { - /* - * We start at the higher value out of 16kiB and just after the - * mft bitmap. - */ - opt.mft_lcn = rl_mft_bmp[0].lcn + rl_mft_bmp[0].length; - if (opt.mft_lcn * vol->cluster_size < 16 * 1024) - opt.mft_lcn = (16 * 1024 + vol->cluster_size - 1) / - vol->cluster_size; - } - Dprintf("$MFT logical cluster number = 0x%x\n", opt.mft_lcn); - /* Determine MFT zone size. */ - opt.mft_zone_end = opt.nr_clusters; - switch (opt.mft_zone_multiplier) { /* % of volume size in clusters */ - case 4: - opt.mft_zone_end = opt.mft_zone_end >> 1; /* 50% */ - break; - case 3: - opt.mft_zone_end = opt.mft_zone_end * 3 >> 3; /* 37.5% */ - break; - case 2: - opt.mft_zone_end = opt.mft_zone_end >> 2; /* 25% */ - break; - /* case 1: */ - default: - opt.mft_zone_end = opt.mft_zone_end >> 3; /* 12.5% */ - break; - } - Dprintf("MFT zone size = %lukiB\n", opt.mft_zone_end / 1024); - /* - * The mft zone begins with the mft data attribute, not at the beginning - * of the device. - */ - opt.mft_zone_end += opt.mft_lcn; - /* Create run list for mft. */ - rl_mft = (run_list *)malloc(2 * sizeof(run_list)); - if (!rl_mft) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - rl_mft[0].vcn = 0LL; - rl_mft[0].lcn = opt.mft_lcn; - /* We already rounded mft size up to a cluster. */ - j = opt.mft_size / vol->cluster_size; - rl_mft[1].vcn = rl_mft[0].length = j; - rl_mft[1].lcn = -1LL; - rl_mft[1].length = 0LL; - /* Allocate clusters for mft. */ - for (i = 0; i < j; i++) - ntfs_set_bit(lcn_bitmap, opt.mft_lcn + i, 1); - /* Determine mftmirr_lcn (middle of volume). */ - opt.mftmirr_lcn = (opt.nr_sectors * opt.sector_size >> 1) - / vol->cluster_size; - Dprintf("$MFTMirr logical cluster number = 0x%x\n", opt.mftmirr_lcn); - /* Create run list for mft mirror. */ - rl_mftmirr = (run_list *)malloc(2 * sizeof(run_list)); - if (!rl_mftmirr) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - rl_mftmirr[0].vcn = 0LL; - rl_mftmirr[0].lcn = opt.mftmirr_lcn; - /* - * The mft mirror is either 4kb (the first four records) or one cluster - * in size, which ever is bigger. In either case, it contains a - * byte-for-byte identical copy of the beginning of the mft (i.e. either - * ther first four records (4kb) or the first cluster worth of records, - * whichever is bigger). - */ - j = (4 * vol->mft_record_size + vol->cluster_size - 1) / vol->cluster_size; - rl_mftmirr[1].vcn = rl_mftmirr[0].length = j; - rl_mftmirr[1].lcn = -1LL; - rl_mftmirr[1].length = 0LL; - /* Allocate clusters for mft mirror. */ - for (i = 0; i < j; i++) - ntfs_set_bit(lcn_bitmap, opt.mftmirr_lcn + i, 1); - opt.logfile_lcn = opt.mftmirr_lcn + j; - Dprintf("$LogFile logical cluster number = 0x%x\n", opt.logfile_lcn); - /* Create run list for log file. */ - rl_logfile = (run_list *)malloc(2 * sizeof(run_list)); - if (!rl_logfile) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - rl_logfile[0].vcn = 0LL; - rl_logfile[0].lcn = opt.logfile_lcn; - /* - * Determine logfile_size from volume_size (rounded up to a cluster), - * making sure it does not overflow the end of the volume. - */ - if (opt.volume_size < 2048LL * 1024) /* < 2MiB */ - opt.logfile_size = 256LL * 1024; /* -> 256kiB */ - else if (opt.volume_size < 4000000LL) /* < 4MB */ - opt.logfile_size = 512LL * 1024; /* -> 512kiB */ - else if (opt.volume_size <= 200LL * 1024 * 1024)/* < 200MiB */ - opt.logfile_size = 2048LL * 1024; /* -> 2MiB */ - else if (opt.volume_size >= 400LL << 20) /* > 400MiB */ - opt.logfile_size = 4 << 20; /* -> 4MiB */ - else - opt.logfile_size = (opt.volume_size / 100) & - ~(vol->cluster_size - 1); - j = opt.logfile_size / vol->cluster_size; - while (rl_logfile[0].lcn + j >= opt.nr_clusters) { - /* - * $Logfile would overflow volume. Need to make it smaller than - * the standard size. It's ok as we are creating a non-standard - * volume anyway if it is that small. - */ - opt.logfile_size >>= 1; - j = opt.logfile_size / vol->cluster_size; - } - opt.logfile_size = (opt.logfile_size + vol->cluster_size - 1) & - ~(vol->cluster_size - 1); - Dprintf("$LogFile (journal) size = %ikiB\n", opt.logfile_size / 1024); - /* - * FIXME: The 256kiB limit is arbitrary. Should find out what the real - * minimum requirement for Windows is so it doesn't blue screen. - */ - if (opt.logfile_size < 256 << 10) - err_exit("$LogFile would be created with invalid size. This " - "is not allowed as it would cause Windows to " - "blue screen and during boot.\n"); - rl_logfile[1].vcn = rl_logfile[0].length = j; - rl_logfile[1].lcn = -1LL; - rl_logfile[1].length = 0LL; - /* Allocate clusters for log file. */ - for (i = 0; i < j; i++) - ntfs_set_bit(lcn_bitmap, opt.logfile_lcn + i, 1); - /* Create run list for $Boot. */ - rl_boot = (run_list *)malloc(2 * sizeof(run_list)); - if (!rl_boot) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - rl_boot[0].vcn = 0LL; - rl_boot[0].lcn = 0LL; - /* - * $Boot is always 8192 (0x2000) bytes or 1 cluster, whichever is - * bigger. - */ - j = (8192 + vol->cluster_size - 1) / vol->cluster_size; - rl_boot[1].vcn = rl_boot[0].length = j; - rl_boot[1].lcn = -1LL; - rl_boot[1].length = 0LL; - /* Allocate clusters for $Boot. */ - for (i = 0; i < j; i++) - ntfs_set_bit(lcn_bitmap, 0LL + i, 1); - /* Allocate a buffer large enough to hold the mft. */ - buf = calloc(1, opt.mft_size); - if (!buf) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - /* Create run list for $BadClus, $DATA named stream $Bad. */ - rl_bad = (run_list *)malloc(2 * sizeof(run_list)); - if (!rl_bad) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - rl_bad[0].vcn = 0LL; - rl_bad[0].lcn = -1LL; - /* - * $BadClus named stream $Bad contains the whole volume as a single - * sparse run list entry. - */ - rl_bad[1].vcn = rl_bad[0].length = opt.nr_clusters; - rl_bad[1].lcn = -1LL; - rl_bad[1].length = 0LL; - - // TODO: Mark bad blocks as such. - - /* - * If not quick format, fill the device with 0s. - * FIXME: Except bad blocks! (AIA) - */ - if (!opt.quick_format) { - unsigned long position; - unsigned long mid_clust; - float progress_inc = (float)opt.nr_clusters / 100; - - Qprintf("Initialising device with zeroes: 0%%"); - fflush(stdout); - mid_clust = (opt.volume_size >> 1) / vol->cluster_size; - for (position = 0; position < opt.nr_clusters; position++) { - if (!(position % (int)(progress_inc+1))) { - Qprintf("\b\b\b\b%3.0f%%", position / - progress_inc); - fflush(stdout); - } - bw = mkntfs_write(vol->fd, buf, vol->cluster_size); - if (bw != vol->cluster_size) { - if (bw != -1 || errno != EIO) - err_exit("This should not happen.\n"); - if (!position) - err_exit("Error: Cluster zero is bad. " - "Cannot create NTFS file " - "system.\n"); - if (position == mid_clust && - (vol->major_ver < 1 || - (vol->major_ver == 1 && - vol->minor_ver < 2))) - err_exit("Error: Bad cluster found in " - "location reserved for system " - "file $Boot.\n"); - /* Add the baddie to our bad blocks list. */ - append_to_bad_blocks(position); - Qprintf("\nFound bad cluster (%ld). Adding to " - "list of bad blocks.\nInitialising " - "device with zeroes: %3.0i%%", position, - position / progress_inc); - /* Seek to next cluster. */ - lseek(vol->fd, ((off_t)position + 1) * - vol->cluster_size, SEEK_SET); - } - } - Qprintf("\b\b\b\b100%%"); - position = (opt.volume_size & (vol->cluster_size - 1)) / - opt.sector_size; - for (i = 0; i < position; i++) { - bw = mkntfs_write(vol->fd, buf, opt.sector_size); - if (bw != opt.sector_size) { - if (bw != -1 || errno != EIO) - err_exit("This should not happen.\n"); - else if (i + 1 == position && - (vol->major_ver >= 2 || - (vol->major_ver == 1 && - vol->minor_ver >= 2))) - err_exit("Error: Bad cluster found in " - "location reserved for system " - "file $Boot.\n"); - /* Seek to next sector. */ - lseek(vol->fd, opt.sector_size, SEEK_CUR); - } - } - Qprintf(" - Done.\n"); - } - Qprintf("Creating NTFS volume structures.\n"); - /* Setup an empty mft record. */ - format_mft_record((MFT_RECORD*)buf); - /* - * Copy the mft record onto all 16 records in the buffer and setup the - * sequence numbers of each system file to equal the mft record number - * of that file (only for $MFT is the sequence number 1 rather than 0). - */ - for (i = 1; i < 16; i++) { - m = (MFT_RECORD*)(buf + i * vol->mft_record_size); - memcpy(m, buf, vol->mft_record_size); - m->sequence_number = cpu_to_le16(i); - } - /* - * If a cluster contains more than the 16 system files, fill the rest - * with empty, formatted records. - */ - if (vol->cluster_size > 16 * vol->mft_record_size) { - for (i = 16; i * vol->mft_record_size < vol->cluster_size; i++) - memcpy(buf + i * vol->mft_record_size, buf, - vol->mft_record_size); - } - /* - * Create the 16 system files, adding the system information attribute - * to each as well as marking them in use in the mft bitmap. - */ - for (i = 0; i < 16; i++) { - u32 file_attrs; - - m = (MFT_RECORD*)(buf + i * vol->mft_record_size); - m->flags |= MFT_RECORD_IN_USE; - ntfs_set_bit(mft_bitmap, 0LL + i, 1); - file_attrs = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM; - if (i == FILE_root) { - if (opt.disable_indexing) - file_attrs |= FILE_ATTR_NOT_CONTENT_INDEXED; - if (opt.enable_compression) - file_attrs |= FILE_ATTR_COMPRESSED; - } - add_attr_std_info(m, file_attrs); - // dump_mft_record(m); - } - /* The root directory mft reference. */ - root_ref = MAKE_MFT_REF(FILE_root, FILE_root); - Vprintf("Creating root directory (mft record 5)\n"); - m = (MFT_RECORD*)(buf + 5 * vol->mft_record_size); - m->flags |= MFT_RECORD_IS_DIRECTORY; - m->link_count = cpu_to_le16(le16_to_cpu(m->link_count) + 1); - err = add_attr_file_name(m, root_ref, 0LL, 0LL, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | - FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT, 0, 0, - ".", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_root, &sd, &i); - err = add_attr_sd(m, sd, i); - } - // FIXME: This should be IGNORE_CASE - if (!err) - err = add_attr_index_root(m, "$I30", 4, 0, AT_FILE_NAME, - COLLATION_FILE_NAME, opt.index_block_size); - // FIXME: This should be IGNORE_CASE - if (!err) - err = upgrade_to_large_index(m, "$I30", 4, 0, &index_block); - if (!err) { - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) - err_exit("Failed to allocate attribute search " - "context: %s\n", strerror(errno)); - /* There is exactly one file name so this is ok. */ - if (ntfs_lookup_attr(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx)) { - ntfs_put_attr_search_ctx(ctx); - err_exit("BUG: $FILE_NAME attribute not found.\n"); - } - a = ctx->attr; - err = insert_file_link_in_dir_index(index_block, root_ref, - (FILE_NAME_ATTR*)((char*)a + - le16_to_cpu(a->value_offset)), - le32_to_cpu(a->value_length)); - ntfs_put_attr_search_ctx(ctx); - } - if (err) - err_exit("Couldn't create root directory: %s\n", - strerror(-err)); - // dump_mft_record(m); - /* Add all other attributes, on a per-file basis for clarity. */ - Vprintf("Creating $MFT (mft record 0)\n"); - m = (MFT_RECORD*)buf; - err = add_attr_data_positioned(m, NULL, 0, 0, 0, rl_mft, buf, - opt.mft_size); - if (!err) - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_MFT, 1), opt.mft_size, - opt.mft_size, FILE_ATTR_HIDDEN | - FILE_ATTR_SYSTEM, 0, 0, "$MFT", - FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_MFT, &sd, &i); - err = add_attr_sd(m, sd, i); - } - /* mft_bitmap is not modified in mkntfs; no need to sync it later. */ - if (!err) - err = add_attr_bitmap_positioned(m, NULL, 0, 0, rl_mft_bmp, - mft_bitmap, mft_bitmap_byte_size); - if (err < 0) - err_exit("Couldn't create $MFT: %s\n", strerror(-err)); - //dump_mft_record(m); - Vprintf("Creating $MFTMirr (mft record 1)\n"); - m = (MFT_RECORD*)(buf + 1 * vol->mft_record_size); - err = add_attr_data_positioned(m, NULL, 0, 0, 0, rl_mftmirr, buf, - rl_mftmirr[0].length * vol->cluster_size); - if (!err) - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_MFTMirr, FILE_MFTMirr), - rl_mftmirr[0].length * vol->cluster_size, - rl_mftmirr[0].length * vol->cluster_size, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$MFTMirr", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_MFTMirr, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) - err_exit("Couldn't create $MFTMirr: %s\n", strerror(-err)); - //dump_mft_record(m); - Vprintf("Creating $LogFile (mft record 2)\n"); - m = (MFT_RECORD*)(buf + 2 * vol->mft_record_size); - buf2 = malloc(opt.logfile_size); - if (!buf2) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - memset(buf2, -1, opt.logfile_size); - err = add_attr_data_positioned(m, NULL, 0, 0, 0, rl_logfile, buf2, - opt.logfile_size); - free(buf2); - buf2 = NULL; - if (!err) - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_LogFile, FILE_LogFile), - opt.logfile_size, opt.logfile_size, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$LogFile", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_LogFile, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) - err_exit("Couldn't create $LogFile: %s\n", strerror(-err)); - //dump_mft_record(m); - Vprintf("Creating $Volume (mft record 3)\n"); - m = (MFT_RECORD*)(buf + 3 * vol->mft_record_size); - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_Volume, FILE_Volume), 0LL, 0LL, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$Volume", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_Volume, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (!err) - err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); - if (!err) - err = add_attr_vol_name(m, vol->vol_name, vol->vol_name ? - strlen(vol->vol_name) : 0); - if (!err) { - Qprintf("Setting the volume dirty so check disk runs on next " - "reboot into Windows.\n"); - err = add_attr_vol_info(m, VOLUME_IS_DIRTY, vol->major_ver, - vol->minor_ver); - } - if (err < 0) - err_exit("Couldn't create $Volume: %s\n", strerror(-err)); - //dump_mft_record(m); - Vprintf("Creating $AttrDef (mft record 4)\n"); - m = (MFT_RECORD*)(buf + 4 * vol->mft_record_size); - if (vol->major_ver < 3) - buf2_size = 36000; - else - buf2_size = opt.attr_defs_len; - buf2 = (char*)calloc(1, buf2_size); - if (!buf2) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - memcpy(buf2, opt.attr_defs, opt.attr_defs_len); - err = add_attr_data(m, NULL, 0, 0, 0, buf2, buf2_size); - free(buf2); - buf2 = NULL; - if (!err) - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_AttrDef, FILE_AttrDef), - (buf2_size + vol->cluster_size - 1) & - ~(vol->cluster_size - 1), buf2_size, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$AttrDef", FILE_NAME_WIN32_AND_DOS); - buf2_size = 0; - if (!err) { - init_system_file_sd(FILE_AttrDef, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) - err_exit("Couldn't create $AttrDef: %s\n", strerror(-err)); - //dump_mft_record(m); - Vprintf("Creating $Bitmap (mft record 6)\n"); - m = (MFT_RECORD*)(buf + 6 * vol->mft_record_size); - err = add_attr_data(m, NULL, 0, 0, 0, lcn_bitmap, lcn_bitmap_byte_size); - if (!err) - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_Bitmap, FILE_Bitmap), - (lcn_bitmap_byte_size + vol->cluster_size - 1) & - ~(vol->cluster_size - 1), lcn_bitmap_byte_size, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$Bitmap", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_Bitmap, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) - err_exit("Couldn't create $Bitmap: %s\n", strerror(-err)); - //dump_mft_record(m); - Vprintf("Creating $Boot (mft record 7)\n"); - m = (MFT_RECORD*)(buf + 7 * vol->mft_record_size); - buf2 = calloc(1, 8192); - if (!buf2) - err_exit("Failed to allocate internal buffer: %s\n", - strerror(errno)); - memcpy(buf2, boot_array, sizeof(boot_array)); - /* - * Create the boot sector into buf2. Note, that buf2 already is zeroed - * in the boot sector section and that it has the NTFS OEM id/magic - * already inserted, so no need to worry about these things. - */ - bs = (NTFS_BOOT_SECTOR*)buf2; - bs->bpb.bytes_per_sector = cpu_to_le16(opt.sector_size); - bs->bpb.sectors_per_cluster = (u8)(vol->cluster_size / - opt.sector_size); - bs->bpb.media_type = 0xf8; /* hard disk */ - /* - * If there are problems go back to bs->unused[0-3] and set them. See - * ../include/bootsect.h for details. Other fields to also consider - * setting are: bs->bpb.sectors_per_track, .heads, and .hidden_sectors. - */ - bs->number_of_sectors = scpu_to_le64(opt.nr_sectors); - bs->mft_lcn = scpu_to_le64(opt.mft_lcn); - bs->mftmirr_lcn = scpu_to_le64(opt.mftmirr_lcn); - if (vol->mft_record_size >= vol->cluster_size) - bs->clusters_per_mft_record = vol->mft_record_size / - vol->cluster_size; - else { - bs->clusters_per_mft_record = -(ffs(vol->mft_record_size) - 1); - if ((1 << -bs->clusters_per_mft_record) != vol->mft_record_size) - err_exit("BUG: calculated clusters_per_mft_record " - "is wrong (= 0x%x)\n", - bs->clusters_per_mft_record); - } - Dprintf("Clusters per mft record = %i (0x%x)\n", - bs->clusters_per_mft_record, - bs->clusters_per_mft_record); - if (opt.index_block_size >= vol->cluster_size) - bs->clusters_per_index_record = opt.index_block_size / - vol->cluster_size; - else { - bs->clusters_per_index_record = -(ffs(opt.index_block_size) - 1); - if ((1 << -bs->clusters_per_index_record) != - opt.index_block_size) - err_exit("BUG: calculated clusters_per_index_record " - "is wrong (= 0x%x)\n", - bs->clusters_per_index_record); - } - Dprintf("Clusters per index block = %i (0x%x)\n", - bs->clusters_per_index_record, - bs->clusters_per_index_record); - /* Generate a 64-bit random number for the serial number. */ - bs->volume_serial_number = scpu_to_le64(((s64)random() << 32) | - ((s64)random() & 0xffffffff)); - /* - * Leave zero for now as NT4 leaves it zero, too. If want it later, see - * ../libntfs/bootsect.c for how to calculate it. - */ - bs->checksum = cpu_to_le32(0); - /* Make sure the bootsector is ok. */ - if (!is_boot_sector_ntfs(bs, opt.verbose > 0 ? 0 : 1)) - err_exit("FATAL: Generated boot sector is invalid!\n"); - err = add_attr_data_positioned(m, NULL, 0, 0, 0, rl_boot, buf2, 8192); - if (!err) - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_Boot, FILE_Boot), - (8192 + vol->cluster_size - 1) & - ~(vol->cluster_size - 1), 8192, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$Boot", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_Boot, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) - err_exit("Couldn't create $Boot: %s\n", strerror(-err)); - Vprintf("Creating backup boot sector.\n"); - /* - * Write the first max(512, opt.sector_size) bytes from buf2 to the - * last sector. - */ - if (lseek(vol->fd, (opt.nr_sectors + 1) * opt.sector_size - i, - SEEK_SET) == (off_t)-1) - goto bb_err; - bw = mkntfs_write(vol->fd, buf2, i); - free(buf2); - buf2 = NULL; - if (bw != i) { - int _e = errno; - char *_s; - - if (bw == -1LL) - _s = strerror(_e); - else - _s = "unknown error"; - if (bw != -1LL || (bw == -1LL && _e != ENOSPC)) { - err_exit("Couldn't write backup boot sector: %s\n", _s); -bb_err: - Eprintf("Seek failed: %s\n", strerror(errno)); - } - Eprintf("Couldn't write backup boot sector. This is due to a " - "limitation in the\nLinux kernel. This is not " - "a major problem as Windows check disk will " - "create the\nbackup boot sector when it " - "is run on your next boot into Windows.\n"); - } - //dump_mft_record(m); - Vprintf("Creating $BadClus (mft record 8)\n"); - m = (MFT_RECORD*)(buf + 8 * vol->mft_record_size); - // FIXME: This should be IGNORE_CASE - /* Create a sparse named stream of size equal to the volume size. */ - err = add_attr_data_positioned(m, "$Bad", 4, 0, 0, rl_bad, NULL, - opt.nr_clusters * vol->cluster_size); - if (!err) { - err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); - } - if (!err) { - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_BadClus, FILE_BadClus), - 0LL, 0LL, FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, - 0, 0, "$BadClus", FILE_NAME_WIN32_AND_DOS); - } - if (!err) { - init_system_file_sd(FILE_BadClus, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) - err_exit("Couldn't create $BadClus: %s\n", strerror(-err)); - //dump_mft_record(m); - Vprintf("Creating $Quota (mft record 9)\n"); - m = (MFT_RECORD*)(buf + 9 * vol->mft_record_size); - err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); - if (!err) - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(9, 9), 0LL, 0LL, FILE_ATTR_HIDDEN - | FILE_ATTR_SYSTEM, 0, 0, "$Quota", - FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_Secure, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) - err_exit("Couldn't create $Quota: %s\n", strerror(-err)); - //dump_mft_record(m); - Vprintf("Creating $UpCase (mft record 0xa)\n"); - m = (MFT_RECORD*)(buf + 0xa * vol->mft_record_size); - err = add_attr_data(m, NULL, 0, 0, 0, (char*)vol->upcase, - vol->upcase_len << 1); - if (!err) - err = create_hardlink(index_block, root_ref, m, - MAKE_MFT_REF(FILE_UpCase, FILE_UpCase), - ((vol->upcase_len << 1) + vol->cluster_size - 1) & - ~(vol->cluster_size - 1), vol->upcase_len << 1, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$UpCase", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_UpCase, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) - err_exit("Couldn't create $UpCase: %s\n", strerror(-err)); - //dump_mft_record(m); - /* NTFS 1.2 reserved system files (mft records 0xb-0xf) */ - for (i = 0xb; i < 0x10; i++) { - Vprintf("Creating system file (mft record 0x%x)\n", i, i); - m = (MFT_RECORD*)(buf + i * vol->mft_record_size); - err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); - if (!err) { - init_system_file_sd(i, &sd, &j); - err = add_attr_sd(m, sd, j); - } - if (err < 0) - err_exit("Couldn't create system file %i (0x%x): %s\n", - i, i, strerror(-err)); - //dump_mft_record(m); - } -// - Do not step onto bad blocks!!! -// - If any bad blocks were specified or found, modify $BadClus, allocating the -// bad clusters in $Bitmap. -// - C&w bootsector backup bootsector (backup in last sector of the -// partition). -// - If NTFS 3.0+, c&w $Secure file and $Extend directory with the -// corresponding special files in it, i.e. $ObjId, $Quota, $Reparse, and -// $UsnJrnl. And others? Or not all necessary? -// - RE: Populate $root with the system files (and $Extend directory if -// applicable). Possibly should move this as far to the top as possible and -// update during each subsequent c&w of each system file. - Vprintf("Syncing root directory index record.\n"); - m = (MFT_RECORD*)(buf + 5 * vol->mft_record_size); - i = 5 * sizeof(uchar_t); - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) - err_exit("Failed to allocate attribute search context: %s\n", - strerror(errno)); - // FIXME: This should be IGNORE_CASE! - if (ntfs_lookup_attr(AT_INDEX_ALLOCATION, I30, 4, 0, 0, - NULL, 0, ctx)) { - ntfs_put_attr_search_ctx(ctx); - err_exit("BUG: $INDEX_ALLOCATION attribute not found.\n"); - } - a = ctx->attr; - rl_index = ntfs_decompress_mapping_pairs(vol, a, NULL); - if (!rl_index) { - ntfs_put_attr_search_ctx(ctx); - err_exit("Failed to decompress run list of $INDEX_ALLOCATION " - "attribute.\n"); - } - if (sle64_to_cpu(a->initialized_size) < i) { - ntfs_put_attr_search_ctx(ctx); - err_exit("BUG: $INDEX_ALLOCATION attribute too short.\n"); - } - ntfs_put_attr_search_ctx(ctx); - i = sizeof(INDEX_BLOCK) - sizeof(INDEX_HEADER) + - le32_to_cpu(index_block->index.allocated_size); - err = ntfs_pre_write_mst_fixup((NTFS_RECORD*)index_block, i); - if (err) - err_exit("ntfs_pre_write_mst_fixup() failed while syncing " - "root directory index block.\n"); - lw = ntfs_rlwrite(vol->fd, rl_index, (char*)index_block, i, NULL); - if (lw != i) - err_exit("Error writing $INDEX_ALLOCATION.\n"); - /* No more changes to @index_block below here so no need for fixup: */ - // ntfs_post_write_mst_fixup((NTFS_RECORD*)index_block); - Vprintf("Syncing $Bitmap.\n"); - m = (MFT_RECORD*)(buf + 6 * vol->mft_record_size); - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) - err_exit("Failed to allocate attribute search context: %s\n", - strerror(errno)); - if (ntfs_lookup_attr(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - ntfs_put_attr_search_ctx(ctx); - err_exit("BUG: $DATA attribute not found.\n"); - } - a = ctx->attr; - if (a->non_resident) { - rl = ntfs_decompress_mapping_pairs(vol, a, NULL); - ntfs_put_attr_search_ctx(ctx); - if (!rl) - err_exit("ntfs_decompress_mapping_pairs() failed\n"); - lw = ntfs_rlwrite(vol->fd, rl, lcn_bitmap, - lcn_bitmap_byte_size, NULL); - if (lw != lcn_bitmap_byte_size) - err_exit("%s\n", lw == -1 ? strerror(errno) : - "unknown error"); - } else { - memcpy((char*)a + le16_to_cpu(a->value_offset), lcn_bitmap, - le32_to_cpu(a->value_length)); - ntfs_put_attr_search_ctx(ctx); - } - /* - * No need to sync $MFT/$BITMAP as that has never been modified since - * its creation. - */ - Vprintf("Syncing $MFT.\n"); - pos = opt.mft_lcn * vol->cluster_size; - lw = 1; - for (i = 0; i < opt.mft_size / vol->mft_record_size; i++) { - if (!opt.no_action) - lw = ntfs_mst_pwrite(vol->fd, pos, 1, - vol->mft_record_size, - buf + i * vol->mft_record_size); - if (lw != 1) - err_exit("%s\n", lw == -1 ? strerror(errno) : - "unknown error"); - pos += vol->mft_record_size; - } - Vprintf("Updating $MFTMirr.\n"); - pos = opt.mftmirr_lcn * vol->cluster_size; - lw = 1; - for (i = 0; i < rl_mftmirr[0].length * vol->cluster_size / - vol->mft_record_size; i++) { - u16 usn, *usnp; - m = (MFT_RECORD*)(buf + i * vol->mft_record_size); - /* - * Decrement the usn by one, so it becomes the same as the one - * in $MFT once it is mst protected. - This is as we need the - * $MFTMirr to have the exact same byte by byte content as - * $MFT, rather than just equivalent meaning content. - */ - usnp = (u16*)((char*)m + le16_to_cpu(m->usa_ofs)); - usn = le16_to_cpup(usnp); - if (usn-- <= 1) - usn = 0xfffe; - *usnp = cpu_to_le16(usn); - if (!opt.no_action) - lw = ntfs_mst_pwrite(vol->fd, pos, 1, - vol->mft_record_size, - buf + i * vol->mft_record_size); - if (lw != 1) - err_exit("%s\n", lw == -1 ? strerror(errno) : - "unknown error"); - pos += vol->mft_record_size; - } - Vprintf("Syncing device.\n"); - if (fdatasync(vol->fd) == -1) - err_exit("Syncing device. FAILED: %s", strerror(errno)); - Qprintf("mkntfs completed successfully. Have a nice day.\n"); - /* - * Device is unlocked and closed by the registered exit function - * mkntfs_exit(). - */ - return 0; -} - diff --git a/ntfstools/ntfsdump_logfile.c b/ntfstools/ntfsdump_logfile.c deleted file mode 100644 index e785ea96..00000000 --- a/ntfstools/ntfsdump_logfile.c +++ /dev/null @@ -1,371 +0,0 @@ -const char *EXEC_NAME = "NtfsDump_LogFile"; -const char *EXEC_VERSION = "1.0"; -/* - * $Id$ - * - * NtfsDump_LogFile - Part of the Linux-NTFS project. - * - * Copyright (c) 2000,2001 Anton Altaparmakov. - * - * This utility will interpret the contents of the journal ($LogFile) of an - * NTFS partition and display the results on stdout. Errors will be output to - * stderr. - * - * Anton Altaparmakov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS source - * in the file COPYING); if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * WARNING: This program might not work on architectures which do not allow - * unaligned access. For those, the program would need to start using - * get/put_unaligned macros (#include ), but not doing it yet, - * since NTFS really mostly applies to ia32 only, which does allow unaligned - * accesses. We might not actually have a problem though, since the structs are - * defined as being packed so that might be enough for gcc to insert the - * correct code. - * - * If anyone using a non-little endian and/or an aligned access only CPU tries - * this program please let me know whether it works or not! - * - * Anton Altaparmakov - */ - -#include -#include -#include -#include -#include -#include - -#include "types.h" -#include "attrib.h" -#include "mft.h" -#include "disk_io.h" -#include "logfile.h" -#include "mst.h" - -int main(int argc, char **argv) -{ - MFT_RECORD *m = NULL; - ATTR_RECORD *a; - s64 l; - unsigned char *lfd = NULL; - ntfs_volume *vol = NULL; - ntfs_attr_search_ctx *ctx = NULL; - RESTART_PAGE_HEADER *rph; - RESTART_AREA *rr; - RESTART_CLIENT *cr; - RECORD_PAGE_HEADER *rcrd_ph; - LOG_RECORD *lr; - int pass = 1; - int i, lps, client; - char zero[4096]; - - memset(zero, 0, sizeof(zero)); - printf("\n"); - if (argc != 2) { - printf("%s v%s - Interpret and display information about the " - "journal\n($LogFile) of an NTFS volume.\n\n" - /* Generic copyright / disclaimer. */ - "Copyright (c) 2000, 2001 Anton Altaparmakov.\n\n" - "%s is free software, released under the GNU " - "General Public License\nand you are welcome to " - "redistribute it under certain conditions.\n" - "%s comes with ABSOLUTELY NO WARRANTY; for details " - "read the GNU\nGeneral Public License to be found " - "in the file COPYING in the main Linux-NTFS\n" - "distribution directory.\n\n" - /* Generic part ends here. */ - "Syntax: ntfsdump_logfile partition_or_file_name\n" - " e.g. ntfsdump_logfile /dev/hda6\n\n", - EXEC_NAME, EXEC_VERSION, EXEC_NAME, EXEC_NAME); - fprintf(stderr, "Error: incorrect syntax\n"); - exit(1); - } - vol = ntfs_mount(argv[1], MS_RDONLY); - if (!vol) { - perror("ntfs_mount(MS_RDONLY) failed"); - exit(1); - } - /* Check NTFS version is ok for us. */ - printf("\nNTFS volume version is %i.%i.\n", vol->major_ver, - vol->minor_ver); - switch (vol->major_ver) { - case 1: - if (vol->minor_ver == 1 || vol->minor_ver == 2) - break; - else - goto version_error; - case 2: case 3: - if (vol->minor_ver == 0) - break; - /* Fall through on error. */ - default: -version_error: - fprintf(stderr, "Error: Unknown NTFS version.\n"); - goto error_exit; - } - /* Read in $LogFile. */ - if (ntfs_read_file_record(vol, FILE_LogFile, &m, NULL)) { - fprintf(stderr, "Error reading mft record for $LogFile.\n"); - goto error_exit; - } - if (!(m->flags & MFT_RECORD_IN_USE)) { - fprintf(stderr, "Error: $LogFile has been deleted. Run chkdsk " - "to fix this.\n"); - goto error_exit; - } - ctx = ntfs_get_attr_search_ctx(NULL, m); - if (!ctx) { - perror("Failed to allocate attribute search context"); - goto error_exit; - } - /* Find the $DATA attribute of the $LogFile. */ - if (ntfs_lookup_attr(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - fprintf(stderr, "Error: Attribute $DATA was not found in" \ - "$LogFile!\n"); - goto log_file_error; - } - a = ctx->attr; - /* Get length of $LogFile contents. */ - l = get_attribute_value_length(a); - if (!l) { - puts("$LogFile has zero length, no need to write to disk."); - goto log_file_error; - } - /* Allocate a buffer to hold all of the $LogFile contents. */ - lfd = (unsigned char*)malloc(l); - if (!lfd) { - puts("Not enough memory to load $LogFile."); - goto log_file_error; - } - /* Read in the $LogFile into the buffer. */ - if (l != get_attribute_value(vol, m, a, lfd)) { - puts("Amount of data read does not correspond to expected " - "length!"); - free(lfd); - goto log_file_error; - } - /* Check restart area. */ - if (!is_rstr_recordp(lfd)) { - s64 _l; - - for (_l = 0LL; _l < l; _l++) - if (lfd[_l] != (unsigned char)-1) - break; - if (_l < l) - puts("$LogFile contents are corrupt (magic RSTR " - "missing)!"); - else - puts("$LogFile is empty."); - goto log_file_error; - } - /* Do the interpretation and display now. */ - rph = (RESTART_PAGE_HEADER*)lfd; - lps = le32_to_cpu(rph->log_page_size); -pass_loc: - if (ntfs_post_read_mst_fixup((NTFS_RECORD*)rph, lps) || - is_baad_record(rph->magic)) { - puts("$LogFile incomplete multi sector transfer detected! " - "Cannot handle this yet!"); - goto log_file_error; - } - if ((pass == 2) && !memcmp(lfd, rph, lps)) { - printf("2nd restart area fully matches the 1st one. Skipping " - "display.\n"); - goto skip_rstr_pass; - } - if (le16_to_cpu(rph->major_ver != 1) || - le16_to_cpu(rph->minor_ver != 1)) { - fprintf(stderr, "$LogFile version %i.%i! Error: Unknown " - "$LogFile version!\n", - le16_to_cpu(rph->major_ver), - le16_to_cpu(rph->minor_ver)); - goto log_file_error; - } - rr = (RESTART_AREA*)((char*)rph + le16_to_cpu(rph->restart_offset)); - cr = (RESTART_CLIENT*)((char*)rr + - le16_to_cpu(rr->client_array_offset)); - /* Dump of the interpreted $LogFile restart area. */ - if (pass == 1) - printf("\n$LogFile version %i.%i.\n", - le16_to_cpu(rph->major_ver), - le16_to_cpu(rph->minor_ver)); - printf("\n%s restart area:\n", pass == 1? "1st": "2nd"); - printf("magic = RSTR\n"); - printf("ChkDskLsn = 0x%Lx\n", sle64_to_cpu(rph->chkdsk_lsn)); - printf("SystemPageSize = %u\n", le32_to_cpu(rph->system_page_size)); - printf("LogPageSize = %u\n", le32_to_cpu(rph->log_page_size)); - printf("RestartOffset = 0x%x\n", le16_to_cpu(rph->restart_offset)); - printf("\n(1st) restart record:\n"); - printf("CurrentLsn = %Lx\n", sle64_to_cpu(rr->current_lsn)); - printf("LogClients = %u\n", le16_to_cpu(rr->log_clients)); - printf("ClientFreeList = %i\n", sle16_to_cpu(rr->client_free_list)); - printf("ClientInUseList = %i\n", sle16_to_cpu(rr->client_in_use_list)); - printf("Flags = 0x%x\n", le16_to_cpu(rr->flags)); - printf("SeqNumberBits = %u (0x%x)\n", le32_to_cpu(rr->seq_number_bits), - le32_to_cpu(rr->seq_number_bits)); - printf("RestartAreaLength = 0x%x\n", - le16_to_cpu(rr->restart_area_length)); - printf("ClientArrayOffset = 0x%x\n", - le16_to_cpu(rr->client_array_offset)); - printf("FileSize = %Lu (0x%Lx)\n", le64_to_cpu(rr->file_size), - le64_to_cpu(rr->file_size)); - if (le64_to_cpu(rr->file_size) != l) - puts("$LogFile restart area indicates a log file size" - "different from the actual size!"); - printf("LastLsnDataLength = 0x%x\n", - le32_to_cpu(rr->last_lsn_data_length)); - printf("RecordLength = 0x%x\n", le16_to_cpu(rr->record_length)); - printf("LogPageDataOffset = 0x%x\n", - le16_to_cpu(rr->log_page_data_offset)); - for (client = 0; client < le16_to_cpu(rr->log_clients); client++) { - printf("\nRestart client record number %i:\n", client); - printf("OldestLsn = 0x%Lx\n", sle64_to_cpu(cr->oldest_lsn)); - printf("ClientRestartLsn = 0x%Lx\n", - sle64_to_cpu(cr->client_restart_lsn)); - printf("PrevClient = %i\n", sle16_to_cpu(cr->prev_client)); - printf("NextClient = %i\n", sle16_to_cpu(cr->next_client)); - printf("SeqNumber = 0x%Lx\n", le64_to_cpu(cr->seq_number)); - printf("ClientNameLength = 0x%x\n", - le32_to_cpu(cr->client_name_length)); - if (le32_to_cpu(cr->client_name_length)) { - // convert to ascii and print out. - // printf("ClientName = %u\n", le16_to_cpu(cr->client_name)); - } - /* Size of a restart client record is fixed at 0xa0 bytes. */ - cr = (RESTART_CLIENT*)((char*)cr + 0xa0); - } -skip_rstr_pass: - if (pass == 1) { - rph = (RESTART_PAGE_HEADER*)((char*)rph + lps); - ++pass; - goto pass_loc; - } - rcrd_ph = (RECORD_PAGE_HEADER*)rph; - /* Reuse pass for log record clienter. */ - pass = 0; - printf("\nFinished with restart area. Beginning with log area.\n"); -rcrd_pass_loc: - rcrd_ph = (RECORD_PAGE_HEADER*)((char*)rcrd_ph + lps); - if ((char*)rcrd_ph + lps > (char*)lfd + l) - goto end_of_rcrd_passes; - printf("\nLog record page number %i", pass); - if (!is_rcrd_record(rcrd_ph->magic)) { - for (i = 0; i < lps; i++) - if (((char*)rcrd_ph)[i] != (char)-1) - break; - if (i < lps) - puts(" is corrupt (magic RCRD is missing)."); - else - puts(" is empty."); - pass++; - goto rcrd_pass_loc; - } else - printf(":"); - /* Dump log record page */ - printf("\nmagic = RCRD\n"); - printf("copy.last_lsn/file_offset = 0x%Lx\n", - le64_to_cpu(rcrd_ph->copy.last_lsn)); - printf("flags = 0x%x\n", le32_to_cpu(rcrd_ph->flags)); - printf("page count = %i\n", le16_to_cpu(rcrd_ph->page_count)); - printf("page position = %i\n", le16_to_cpu(rcrd_ph->page_position)); - printf("header.next_record_offset = 0x%Lx\n", - le64_to_cpu(rcrd_ph->header.packed.next_record_offset)); - printf("header.last_end_lsn = 0x%Lx\n", - le64_to_cpu(rcrd_ph->header.packed.last_end_lsn)); - /* - * Where does the 0x40 come from? Is it just usa_offset + - * usa_client * 2 + 7 & ~7 or is it derived from somewhere? - */ - lr = (LOG_RECORD*)((char*)rcrd_ph + 0x40); - client = 0; -log_record_pass: - printf("\nLog record %i:\n", client); - printf("this lsn = 0x%Lx\n", le64_to_cpu(lr->this_lsn)); - printf("client previous lsn = 0x%Lx\n", - le64_to_cpu(lr->client_previous_lsn)); - printf("client undo next lsn = 0x%Lx\n", - le64_to_cpu(lr->client_undo_next_lsn)); - printf("client data length = 0x%x\n", - le32_to_cpu(lr->client_data_length)); - printf("client_id.seq_number = 0x%x\n", - le16_to_cpu(lr->client_id.seq_number)); - printf("client_id.client_index = 0x%x\n", - le16_to_cpu(lr->client_id.client_index)); - printf("record type = 0x%x\n", le32_to_cpu(lr->record_type)); - printf("transaction_id = 0x%x\n", le32_to_cpu(lr->transaction_id)); - printf("flags = 0x%x:", lr->flags); - if (!lr->flags) - printf(" NONE\n"); - else { - int _b = 0; - - if (lr->flags & LOG_RECORD_MULTI_PAGE) { - printf(" LOG_RECORD_MULTI_PAGE"); - _b = 1; - } - if (lr->flags & ~LOG_RECORD_MULTI_PAGE) { - if (_b) - printf(" |"); - printf(" Unknown flags"); - } - printf("\n"); - } - printf("redo_operation = 0x%x\n", le16_to_cpu(lr->redo_operation)); - printf("undo_operation = 0x%x\n", le16_to_cpu(lr->undo_operation)); - printf("redo_offset = 0x%x\n", le16_to_cpu(lr->redo_offset)); - printf("redo_length = 0x%x\n", le16_to_cpu(lr->redo_length)); - printf("undo_offset = 0x%x\n", le16_to_cpu(lr->undo_offset)); - printf("undo_length = 0x%x\n", le16_to_cpu(lr->undo_length)); - printf("target_attribute = 0x%x\n", le16_to_cpu(lr->target_attribute)); - printf("lcns_to_follow = 0x%x\n", le16_to_cpu(lr->lcns_to_follow)); - printf("record_offset = 0x%x\n", le16_to_cpu(lr->record_offset)); - printf("attribute_offset = 0x%x\n", le16_to_cpu(lr->attribute_offset)); - printf("target_vcn = 0x%Lx\n", sle64_to_cpu(lr->target_vcn)); - if (le16_to_cpu(lr->lcns_to_follow) > 0) - printf("Array of lcns:\n"); - for (i = 0; i < le16_to_cpu(lr->lcns_to_follow); i++) - printf("lcn_list[%i].lcn = 0x%Lx\n", i, - sle64_to_cpu(lr->lcn_list[i].lcn)); - client++; - lr = (LOG_RECORD*)((char*)lr + 0x70); - if (((char*)lr + 0x70 <= (char*)rcrd_ph + - le64_to_cpu(rcrd_ph->header.packed.next_record_offset))) - goto log_record_pass; - pass++; - goto rcrd_pass_loc; -end_of_rcrd_passes: -log_file_error: - printf("\n"); - /* Set return code to 0. */ - i = 0; -final_exit: - if (lfd) - free(lfd); - if (ctx) - ntfs_put_attr_search_ctx(ctx); - if (m) - free(m); - if (vol && ntfs_umount(vol, 0)) - ntfs_umount(vol, 1); - return i; -error_exit: - i = 1; - goto final_exit; -} - diff --git a/ntfstools/ntfsfix.8.in b/ntfstools/ntfsfix.8.in deleted file mode 100644 index eed93b8a..00000000 --- a/ntfstools/ntfsfix.8.in +++ /dev/null @@ -1,56 +0,0 @@ -.\" Hey, EMACS: -*- nroff -*- -.\" First parameter, NAME, should be all caps -.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection -.\" other parameters are allowed: see man(7), man(1) -.TH NTFSFIX 8 "July 2001" "Linux-NTFS version @VERSION@" -.\" Please adjust this date whenever revising the manpage. -.\" -.\" Some roff macros, for reference: -.\" .nh disable hyphenation -.\" .hy enable hyphenation -.\" .ad l left justify -.\" .ad b justify to both left and right margins -.\" .nf disable filling -.\" .fi enable filling -.\" .br insert line break -.\" .sp insert n+1 empty lines -.\" for manpage-specific macros, see man(7) -.SH NAME -ntfsfix \- tool for fixing NTFS partitions altered by the Linux kernel NTFS driver. -.SH SYNOPSIS -.B ntfsfix -.I device -.SH DESCRIPTION -This manual page documents briefly the -.B ntfsfix -command. -.PP -.\" TeX users may be more comfortable with the \fB\fP and -.\" \fI\fP escape sequences to invode bold face and italics, -.\" respectively. -\fBntfsfix\fP is a program that fixes NTFS partitions altered in any -manner with the Linux NTFS driver. \fBntfsfix\fP is \fBNOT\fP a Linux -version of chkdsk. It only tries to leave the NTFS partition in a -not-so-inconsistent state after the NTFS driver has written to it. -.sp -\fBntfsfix\fP appeared because MS chkdsk is well known for its -stupidity when fixing altered partitions. Because the main problems -are journal files, \fBntfsfix\fP aims to fix those issues. -.sp -Running ntfsfix after mounting NTFS partitions read-write is recommended -for reducing the chance of severe data loss when NT/W2K tries to remount -the affected partition(s). -.sp -In order to use \fBntfsfix\fP you must unmount the NTFS partition, and run -ntfsfix device, where device is the NTFS partition. After this, you can -safely reboot into NT/W2K. Please note that \fBntfsfix\fP is not a -chkdsk-like tool, and so is not guaranteed that it could fix all the -alterations provoked by the NTFS driver. - -.SH SEE ALSO -.BR mkntfs (8). -.br -.SH AUTHOR -This manual page was written by David Martínez Moreno -, for the Debian GNU/Linux -system (but may be used by others). diff --git a/ntfstools/ntfsfix.c b/ntfstools/ntfsfix.c deleted file mode 100644 index e852061b..00000000 --- a/ntfstools/ntfsfix.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * $Id$ - * - * NtfsFix - Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2002 Anton Altaparmakov. - * - * This utility will attempt to fix a partition that has been damaged by the - * current Linux-NTFS driver. It should be run after dismounting a NTFS - * partition that has been mounted read-write under Linux and before rebooting - * into Windows NT/2000. NtfsFix can be run even after Windows has had mounted - * the partition, but it might be too late and irreversible damage to the data - * might have been done already. - * - * Anton Altaparmakov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS source - * in the file COPYING); if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * WARNING: This program might not work on architectures which do not allow - * unaligned access. For those, the program would need to start using - * get/put_unaligned macros (#include ), but not doing it yet, - * since NTFS really mostly applies to ia32 only, which does allow unaligned - * accesses. We might not actually have a problem though, since the structs are - * defined as being packed so that might be enough for gcc to insert the - * correct code. - * - * If anyone using a non-little endian and/or an aligned access only CPU tries - * this program please let me know whether it works or not! - * - * Anton Altaparmakov - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include "types.h" -#include "attrib.h" -#include "mft.h" -#include "disk_io.h" -#include "logfile.h" - -int main(int argc, char **argv) -{ - s64 l, br; - const char *EXEC_NAME = "NtfsFix"; - const char *OK = "OK"; - const char *FAILED = "FAILED"; - unsigned char *m = NULL, *m2 = NULL; - ntfs_volume *vol; - unsigned long mnt_flags; - int i; - u16 flags; - BOOL done, force = FALSE; - - printf("\n"); - if (argc != 2 || !argv[1]) { - printf("%s v%s - Attempt to fix an NTFS partition that " - "has been damaged by the\nLinux NTFS driver. Note that " - "you should run it every time after you have used\nthe " - "Linux NTFS driver to write to an NTFS partition to " - "prevent massive data\ncorruption from happening when " - "Windows mounts the partition.\nIMPORTANT: Run this " - "only *after* unmounting the partition in Linux but " - "*before*\nrebooting into Windows NT/2000/XP or you " - "*will* suffer! - You have been warned!\n\n" - /* Generic copyright / disclaimer. */ - "Copyright (c) 2000-2002 Anton Altaparmakov.\n\n" - "%s is free software, released under the GNU " - "General Public License and you\nare welcome to " - "redistribute it under certain conditions.\n" - "%s comes with ABSOLUTELY NO WARRANTY; for details " - "read the file GNU\nGeneral Public License to be found " - "in the file COPYING in the main Linux-NTFS\n" - "distribution directory.\n\n" - /* Generic part ends here. */ - "Syntax: ntfsfix partition_or_file_name\n" - " e.g. ntfsfix /dev/hda6\n\n", EXEC_NAME, - VERSION, EXEC_NAME, EXEC_NAME); - fprintf(stderr, "Error: incorrect syntax\n"); - exit(1); - } - if (!ntfs_check_if_mounted(argv[1], &mnt_flags)) { - if ((mnt_flags & NTFS_MF_MOUNTED) && - !(mnt_flags & NTFS_MF_READONLY) && !force) { - fprintf(stderr, "Refusing to operate on read-write " - "mounted device %s.\n", argv[1]); - exit(1); - } - } else - fprintf(stderr, "Failed to determine whether %s is mounted: " - "%s\n", argv[1], strerror(errno)); - /* Attempt a full mount first. */ - printf("Mounting volume... "); - vol = ntfs_mount(argv[1], 0); - if (vol) { - puts(OK); - printf("\nProcessing of $MFT and $MFTMirr completed " - "successfully.\n\n"); - goto mount_ok; - } - puts(FAILED); - - puts("Attempting to correct errors."); - - vol = ntfs_startup_volume(argv[1], 0); - if (!vol) { - puts(FAILED); - perror("Failed to startup volume"); - fprintf(stderr, "Volume is corrupt. You should run chkdsk."); - goto error_exit; - } - - puts("Processing $MFT and $MFTMirr."); - - /* Load data from $MFT and $MFTMirr and compare the contents. */ - m = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); - m2 = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); - if (!m || !m2) { - perror("Failed to allocate memory"); - goto error_exit; - } - - printf("Reading $MFT... "); - l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, - vol->mft_record_size, m); - if (l != vol->mftmirr_size) { - puts(FAILED); - if (l != -1) - errno = EIO; - perror("Failed to read $MFT"); - goto error_exit; - } - puts(OK); - - printf("Reading $MFTMirr... "); - l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, - vol->mft_record_size, m2); - if (l != vol->mftmirr_size) { - puts(FAILED); - if (l != -1) - errno = EIO; - perror("Failed to read $MFTMirr"); - goto error_exit; - } - puts(OK); - - /* - * FIXME: Need to actually check the $MFTMirr for being real. Otherwise - * we might corrupt the partition if someone is experimenting with - * software RAID and the $MFTMirr is not actually in the position we - * expect it to be... )-: - * FIXME: We should emit a warning it $MFTMirr is damaged and ask - * user whether to recreate it from $MFT or whether to abort. - The - * warning needs to include the danger of software RAID arrays. - * Maybe we should go as far as to detect whether we are running on a - * MD disk and if yes then bomb out right at the start of the program? - */ - - printf("Comparing $MFTMirr to $MFT... "); - done = FALSE; - for (i = 0; i < vol->mftmirr_size; ++i) { - const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", - "$Volume", "$AttrDef", "root directory", "$Bitmap", - "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; - const char *s; - - if (i < 12) - s = ESTR[i]; - else if (i < 16) - s = "system file"; - else - s = "mft record"; - - if (is_baad_recordp(m + i * vol->mft_record_size)) { - puts("FAILED"); - fprintf(stderr, "$MFT error: Incomplete multi sector " - "transfer detected in %s.\nCannot " - "handle this yet. )-:\n", s); - goto error_exit; - } - if (!is_mft_recordp(m + i * vol->mft_record_size)) { - puts("FAILED"); - fprintf(stderr, "$MFT error: Invalid mft record for " - "%s.\nCannot handle this yet. )-:\n", - s); - goto error_exit; - } - if (is_baad_recordp(m2 + i * vol->mft_record_size)) { - puts("FAILED"); - fprintf(stderr, "$MFTMirr error: Incomplete multi " - "sector transfer detected in %s.\n", s); - goto error_exit; - } - if (!is_mft_recordp(m2 + i * vol->mft_record_size)) { - puts("FAILED"); - fprintf(stderr, "$MFTMirr error: Invalid mft record " - "for %s.\n", s); - goto error_exit; - } - if (memcmp((u8*)m + i * vol->mft_record_size, (u8*)m2 + - i * vol->mft_record_size, - ntfs_get_mft_record_data_size((MFT_RECORD*)( - (u8*)m + i * vol->mft_record_size)))) { - if (!done) { - done = TRUE; - puts(FAILED); - printf("Correcting differences in " - "$MFTMirr... "); - } - br = ntfs_write_mft_record(vol, i, (MFT_RECORD*)(m + - i * vol->mft_record_size)); - if (br) { - puts(FAILED); - perror("Error correcting $MFTMirr"); - goto error_exit; - } - } - } - puts(OK); - - free(m); - free(m2); - m = m2 = NULL; - - printf("Processing of $MFT and $MFTMirr completed successfully.\n\n"); - if (ntfs_umount(vol, 0)) - ntfs_umount(vol, 1); - vol = ntfs_mount(argv[1], 0); - if (!vol) { - perror("Remount failed"); - goto error_exit; - } -mount_ok: - m = NULL; - - /* Check NTFS version is ok for us (in $Volume) */ - printf("NTFS volume version is %i.%i.\n\n", vol->major_ver, - vol->minor_ver); - if (ntfs_is_version_supported(vol)) { - fprintf(stderr, "Error: Unknown NTFS version.\n"); - goto error_exit; - } - - printf("Setting required flags on partition... "); - /* - * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run - * and fix it for us. - */ - flags = vol->flags | VOLUME_IS_DIRTY; - /* If NTFS volume version >= 2.0 then set mounted on NT4 flag. */ - if (vol->major_ver >= 2) - flags |= VOLUME_MOUNTED_ON_NT4; - if (ntfs_set_volume_flags(vol, flags)) { - puts(FAILED); - fprintf(stderr, "Error setting volume flags.\n"); - goto error_exit; - } - puts(OK); - printf("\n"); - - printf("Going to empty the journal ($LogFile)... "); - if (ntfs_reset_logfile(vol)) { - puts(FAILED); - perror("Failed to reset $LogFile"); - goto error_exit; - } - puts(OK); - printf("\n"); - - if (vol->major_ver >= 3) { - /* FIXME: If on NTFS 3.0+, check for presence of the usn journal and - disable it (if present) as Win2k might be unhappy otherwise and Bad - Things(TM) could happen depending on what applications are actually - using it for. */ - } - - /* FIXME: Should we be marking the quota out of date, too? */ - - /* That's all for now! */ - printf("NTFS partition %s was processed successfully.\n", - vol->dev_name); - /* Set return code to 0. */ - i = 0; -final_exit: - if (m) - free(m); - if (m2) - free(m2); - if (vol && ntfs_umount(vol, 0)) - ntfs_umount(vol, 1); - return i; -error_exit: - i = 1; - goto final_exit; -} - diff --git a/ntfstools/ntfsinfo.8.in b/ntfstools/ntfsinfo.8.in deleted file mode 100644 index edec6976..00000000 --- a/ntfstools/ntfsinfo.8.in +++ /dev/null @@ -1,25 +0,0 @@ -.\" -*- nroff -*- -.\" Copyright (c) 2002 Anton Altaparmakov. All Rights Reserved. -.\" This file may be copied under the terms of the GNU Public License. -.\" -.TH NTFSINFO 8 "May 2002" "Linux-NTFS version @VERSION@" -.SH NAME -ntfsinfo \- dump a file's attributes -.SH SYNOPSIS -.B ntfsinfo -.I device -.I inode-number -.SH DESCRIPTION -.B ntfsinfo -will dump the attributes of inode -.IR inode-number . -.PP -.SH AUTHOR -.B ntfsinfo -was written by Matthew J. Fanto (fanto1mj@cmich.edu). -.SH AVAILABILITY -.B ntfsinfo -is part of the linux-ntfs package and is available from -http://linux-ntfs.sourceforge.net/. - - diff --git a/ntfstools/ntfsinfo.c b/ntfstools/ntfsinfo.c deleted file mode 100644 index 45b6d1bb..00000000 --- a/ntfstools/ntfsinfo.c +++ /dev/null @@ -1,195 +0,0 @@ -/*z - * $Id$ - * - * ntfsinfo - Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Matthew J. Fanto - * Copyright (c) 2002 Anton Altaparmakov - * Copyright (c) 2002 Richard Russon - * - * This utility will dump a file's attributes. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include "mft.h" -#include "attrib.h" -#include "layout.h" -#include "inode.h" - -void get_file_attribute_value(const char *dev, long int i); -void print_standard_information_attr(ntfs_attr_search_ctx * ctx); -void print_file_name_attr(ntfs_attr_search_ctx * ctx); - - -#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) - -int main(int argc, char **argv) -{ - const char *AUTHOR = "Matthew J. Fanto"; - const char *EXEC_NAME = "ntfsinfo"; - const char *locale; - long i; - - locale = setlocale(LC_ALL, ""); - if (!locale) { - char *locale; - - locale = setlocale(LC_ALL, NULL); - printf("Failed to set locale, using default (%s).\n", locale); - } else - printf("Using locale %s.\n", locale); - - if (argc < 3 || argc > 4) { - fprintf(stderr, "%s v%s - %s\n", EXEC_NAME, VERSION, AUTHOR); - fprintf(stderr, "Usage: ntfsinfo device inode\n"); - exit(1); - } - - else { - i = atoll(argv[2]); - get_file_attribute_value(argv[1], i); - } - - return 0; -} - -void get_file_attribute_value(const char *dev, long int i) -{ - - MFT_REF mref; - MFT_RECORD *mrec = NULL; - //ATTR_RECORD *attr = NULL; - //FILE_NAME_ATTR *file_name_attr = NULL; - //STANDARD_INFORMATION *standard_information = NULL; - //SECURITY_DESCRIPTOR_RELATIVE *security_descriptor = NULL; - ntfs_attr_search_ctx *ctx = NULL; - ntfs_volume *vol = NULL; - //char *file_name; - ntfs_inode *inode = NULL; - - vol = ntfs_mount(dev, 0); - - mref = (MFT_REF) i; - inode = ntfs_open_inode(vol, mref); - - if (ntfs_read_file_record(vol, mref, &mrec, NULL)) { - perror("Error reading file record!\n"); - exit(1); - } - - ctx = ntfs_get_attr_search_ctx(inode, mrec); - -// print_file_name_attr(ctx); - -// ctx = ntfs_get_attr_search_ctx(inode, mrec); //need to fix this - - print_standard_information_attr(ctx); -} - - -s64 ntfs2time(s64 time) -{ - s64 t; - printf("Original Time: %Li\n",time); - t = time - NTFS_TIME_OFFSET; - t = t / 10000000; - return t; - - -} - -void print_standard_information_attr(ntfs_attr_search_ctx *ctx) -{ - ATTR_RECORD *attr = NULL; - STANDARD_INFORMATION *standard_information_attr = NULL; - - if (ntfs_lookup_attr - (AT_STANDARD_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - perror("Error looking up $STANDARD_INFORMATION!\n"); - exit(1); - } - - attr = ctx->attr; - - standard_information_attr = - (STANDARD_INFORMATION *) ((char *) attr + - le16_to_cpu(attr->value_offset)); - - printf("Creation time: %Li\n", - ntfs2time(standard_information_attr->creation_time)); -/* printf("Last Data Change Time: %Li\n", - ntfs2time(standard_information_attr->last_data_change_time)); - printf("Last MFT Change Time: %Li\n", - ntfs2time(standard_information_attr->last_mft_change_time)); - printf("Last Access Time: %Li\n", - ntfs2time(standard_information_attr->last_access_time)); - printf("Maxium Versions: %d\n", - standard_information_attr->maximum_versions); - printf("Version Number: %d\n", - standard_information_attr->version_number); - printf("Class ID: %d\n", - standard_information_attr->class_id); - printf("Owner ID: %d\n", - standard_information_attr->owner_id); - printf("Security ID: %d\n", - standard_information_attr->security_id); - -*/ -} - -void print_file_name_attr(ntfs_attr_search_ctx *ctx) -{ - ATTR_RECORD *attr = NULL; - ntfs_attr_search_ctx *c = ctx; - FILE_NAME_ATTR *file_name_attr = NULL; - char *file_name; - - if (ntfs_lookup_attr(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - perror("Error looking up $FILE_NAME_ATTR!\n"); - exit(1); - } - - attr = ctx->attr; - ctx = c; - - file_name_attr = - (FILE_NAME_ATTR *) ((char *) attr + - le16_to_cpu(attr->value_offset)); - - file_name = malloc(file_name_attr->file_name_length * sizeof (char)); - - ntfs_ucstombs(file_name_attr->file_name, - file_name_attr->file_name_length, &file_name, - file_name_attr->file_name_length); - - printf("File Name: %s\n", file_name); - printf("File Name Length: %d\n", file_name_attr->file_name_length); - printf("Allocated Size: %Li\n",sle64_to_cpu(file_name_attr->allocated_size)); - printf("Data Size: %Li\n",sle64_to_cpu(file_name_attr->data_size)); -} - -/*void print_security_descriptor_attr(SECURITY_DESCRIPTOR_RELATIVE *security_descriptor) -{ - -}*/ diff --git a/ntfstools/ntfslabel.8.in b/ntfstools/ntfslabel.8.in deleted file mode 100644 index 4b9a05bb..00000000 --- a/ntfstools/ntfslabel.8.in +++ /dev/null @@ -1,54 +0,0 @@ -.\" -*- nroff -*- -.\" Copyright (c) 2002 Anton Altaparmakov. All Rights Reserved. -.\" This file may be copied under the terms of the GNU Public License. -.\" Adapted from e2fsprogs-1.26/misc/e2label.8.in by Theodore Ts'o. -.\" -.TH NTFSLABEL 8 "April 2002" "Linux-NTFS version @VERSION@" -.SH NAME -ntfslabel \- display/change the label on an ntfs file system -.SH SYNOPSIS -.B ntfslabel -.I device -[ -.I new-label -] -.SH DESCRIPTION -.B ntfslabel -will display or change the file system label on the ntfs file system located on -.IR device . -.PP -If the optional argument -.I new-label -is not present, -.B ntfslabel -will simply display the current file system label. -.PP -If the optional argument -.I new-label -is present, then -.B ntfslabel -will set the file system label to be -.IR new-label . -NTFS file system labels can be at most 128 Unicode characters long; if -.I new-label -is longer than 128 Unicode characters, -.B ntfslabel -will truncate it and print a warning message. -.PP -It is also possible to set the file system label using the -.B \-L -option of -.BR mkntfs (8) -during creation of the file system. -.PP -.SH AUTHOR -.B ntfslabel -was written by Matthew J. Fanto (fanto1mj@cmich.edu). This man page was written -by Anton Altaparmakov (aia21@cantab.net). -.SH AVAILABILITY -.B ntfslabel -is part of the linux-ntfs package and is available from -http://linux-ntfs.sourceforge.net/. -.SH SEE ALSO -.BR mkntfs (8) - diff --git a/ntfstools/ntfslabel.c b/ntfstools/ntfslabel.c deleted file mode 100644 index 5d07c0d0..00000000 --- a/ntfstools/ntfslabel.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * $Id$ - * - * ntfslabel - Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Matthew J. Fanto - * Copyright (c) 2002 Anton Altaparmakov - * Copyright (c) 2002 Richard Russon - * - * This utility will display/change the label on an NTFS partition. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "debug.h" -#include "mft.h" - -/* - * print_label - display the current label of a mounted ntfs partition. - * @dev: device to read the label from - * @mnt_flags: mount flags of the device or 0 if not mounted - * @mnt_point: mount point of the device or NULL - * - * Print the label of the device @dev to stdout. - */ -void print_label(const char *dev, const unsigned long mnt_flags, - const char *mnt_point) -{ - ntfs_volume *vol; - - if (mnt_point) { - // Try ioctl and finish if present. - // goto finished; - } - if ((mnt_flags & (NTFS_MF_MOUNTED | NTFS_MF_READONLY)) == - NTFS_MF_MOUNTED) { - fprintf(stderr, "%s is mounted read-write, results may be " - "unreliable.\n", dev); - } - vol = ntfs_mount(dev, MS_RDONLY); - if (!vol) { - fprintf(stderr, "ntfs_mount() on device %s failed: %s\n", dev, - strerror(errno)); - exit(1); - } -//finished: - printf("%s\n", vol->vol_name); - if (ntfs_umount(vol, 0)) - ntfs_umount(vol, 1); -} - -/* - * resize_resident_attribute_value - resize a resident attribute - * @m: mft record containing attribute to resize - * @a: attribute record (inside @m) which to resize - * @new_vsize: the new attribute value size to resize the attribute to - * - * Return 0 on success and -1 with errno = ENOSPC if not enough space in the - * mft record. - */ -int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a, - const u32 new_vsize) -{ - int new_alen, new_muse; - - /* New attribute length and mft record bytes used. */ - new_alen = (le16_to_cpu(a->value_offset) + new_vsize + 7) & ~7; - new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) + - new_alen; - /* Check for sufficient space. */ - if (new_muse > le32_to_cpu(m->bytes_allocated)) { - errno = ENOSPC; - return -1; - } - /* Move attributes behind @a to their new location. */ - memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length), - le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) - - le32_to_cpu(a->length)); - /* Adjust @m to reflect change in used space. */ - m->bytes_in_use = cpu_to_le32(new_muse); - /* Adjust @a to reflect new value size. */ - a->length = cpu_to_le32(new_alen); - a->value_length = cpu_to_le32(new_vsize); - return 0; -} - -/* - * change_label - change the current label on a device - * @dev: device to change the label on - * @mnt_flags: mount flags of the device or 0 if not mounted - * @mnt_point: mount point of the device or NULL - * @label: the new label - * - * Change the label on the device @dev to @label. - */ -void change_label(const char *dev, const unsigned long mnt_flags, - const char *mnt_point, char *label, BOOL force) -{ - ntfs_attr_search_ctx *ctx = NULL; - uchar_t *new_label = NULL; - MFT_RECORD *mrec = NULL; - ATTR_RECORD *a; - ntfs_volume *vol; - int label_len, err = 1; - - if (mnt_point) { - // Try ioctl and return if present. - // return; - } - if (mnt_flags & NTFS_MF_MOUNTED) { - /* If not the root fs or mounted read/write, refuse change. */ - if (!(mnt_flags & NTFS_MF_ISROOT) || - !(mnt_flags & NTFS_MF_READONLY)) { - if (!force) { - fprintf(stderr, "Refusing to change label on " - "read-%s mounted device %s.\n", - mnt_flags & NTFS_MF_READONLY ? - "only" : "write", dev); - return; - } - } - } - vol = ntfs_mount(dev, 0); - if (!vol) { - fprintf(stderr, "ntfs_mount() on device %s failed: %s\n", dev, - strerror(errno)); - exit(1); - } - if (ntfs_read_file_record(vol, (MFT_REF)FILE_Volume, &mrec, NULL)) { - perror("Error reading file record"); - goto err_out; - } - if (!(mrec->flags & MFT_RECORD_IN_USE)) { - fprintf(stderr, "Error: $Volume has been deleted. Run " - "chkdsk to fix this.\n"); - goto err_out; - } - ctx = ntfs_get_attr_search_ctx(NULL, mrec); - if (!ctx) { - perror("Failed to get attribute search context"); - goto err_out; - } - if (ntfs_lookup_attr(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - perror("Lookup of $VOLUME_NAME attribute failed"); - goto err_out; - } - a = ctx->attr; - if (a->non_resident) { - fprintf(stderr, "Error: Attribute $VOLUME_NAME must be " - "resident.\n"); - goto err_out; - } - label_len = ntfs_mbstoucs(label, &new_label, 0); - if (label_len == -1) { - perror("Unable to convert label string to Unicode"); - goto err_out; - } - label_len *= sizeof(uchar_t); - if (label_len > 0x100) { - fprintf(stderr, "New label is too long. Maximum %i characters " - "allowed. Truncating excess characters.\n", - 0x100 / sizeof(uchar_t)); - label_len = 0x100; - new_label[label_len / sizeof(uchar_t)] = cpu_to_le16(L'\0'); - } - if (resize_resident_attribute_value(mrec, a, label_len)) { - perror("Error resizing resident attribute"); - goto err_out; - } - memcpy((char*)a + le16_to_cpu(a->value_offset), new_label, label_len); - if (ntfs_write_mft_record(vol, (MFT_REF)FILE_Volume, mrec)) { - perror("Error writing MFT Record to disk"); - goto err_out; - } - err = 0; -err_out: - if (new_label) - free(new_label); - if (mrec) - free(mrec); - if (ntfs_umount(vol, 0)) - ntfs_umount(vol, 1); - if (err) - exit(1); -} - -int main(int argc, char **argv) -{ - const char *AUTHOR = "Matthew Fanto"; - char *EXEC_NAME = "ntfslabel"; - char *locale, *mnt_point = NULL; - unsigned long mnt_flags; - int err; - // FIXME:Implement option -F meaning force the change. - BOOL force = 0; - - locale = setlocale(LC_ALL, ""); - if (!locale) { - char *locale; - - locale = setlocale(LC_ALL, NULL); - Dprintf("Failed to set locale, using default (%s).\n", locale); - } else - Dprintf("Using locale %s.\n", locale); - if (argc && *argv) - EXEC_NAME = *argv; - if (argc < 2 || argc > 3) { - fprintf(stderr, "%s v%s - %s\n", EXEC_NAME, VERSION, AUTHOR); - fprintf(stderr, "Usage: ntfslabel device [newlabel]\n"); - exit(1); - } - err = ntfs_check_if_mounted(argv[1], &mnt_flags); - if (err) - fprintf(stderr, "Failed to determine whether %s is mounted: " - "%s\n", argv[1], strerror(errno)); - else if (mnt_flags & NTFS_MF_MOUNTED) { - // Not implemented yet. Will be used for ioctl interface to driver. - // mnt_point = ntfs_get_mount_point(argv[1]); - } - if (argc == 2) - print_label(argv[1], mnt_flags, mnt_point); - else - change_label(argv[1], mnt_flags, mnt_point, argv[2], force); - return 0; -} - diff --git a/ntfstools/ntfsresize.8.in b/ntfstools/ntfsresize.8.in deleted file mode 100644 index b2be7c28..00000000 --- a/ntfstools/ntfsresize.8.in +++ /dev/null @@ -1,117 +0,0 @@ -.\" -*- nroff -*- -.\" Copyright 2002 by Szabolcs Szakacsits All Rights Reserved. -.\" -.TH NTFSRESIZE 8 "November 2002" "Linux\-NTFS @VERSION@" -.SH NAME -ntfsresize \- resize an NTFS filesystem -.SH SYNOPSIS -.B ntfsresize -[\fB\-fhin\fR] -[\fB\-s \fIsize\fR[\fBk\fR|\fBM\fR|\fBG\fR]] -.I device -.SH DESCRIPTION -The -.B ntfsresize -program non-destructively resizes Windows NT4, 2000, XP or .NET -NTFS filesystems. At present it can be used to shrink a -defragmented NTFS filesystem located on an unmounted -.I device -(usually a disk partition). The new volume will have -.I size -bytes. -The -.I size -parameter may have one of the optional modifiers -\fBk\fR, \fBM\fR, \fBG\fR, which means the -.I size -parameter is given in kilo-, mega- or gigabytes respectively. -.B ntfsresize -conforms to the SI, ATA, IEEE standards and the disk manufacturers -by using k=10^3, M=10^6 and G=10^9. -.PP -The -.B ntfsresize -program does not manipulate the size of partitions. -If you wish to shrink an NTFS partition, first use -.B ntfsresize -to shrink the size of the filesystem. Then you may use -.BR fdisk (8) -to shrink the size of the partition by deleting the -partition and recreating it with the smaller size. -.PP -.B IMPORTANT! -When recreating the partition with -.BR fdisk (8) -make sure you create it with the same starting -disk cylinder and partition type -as before and you do not make it smaller than the new size -of the NTFS filesystem! Otherwise you may lose your entire filesystem. -Also make sure you set the bootable flag for the partition if it -existed before. Failing to do so you might not be able to boot your -computer from the disk! -.PP -Note, -.B ntfsresize -schedules 'chkdsk' to make an NTFS consistency check -when you will boot Windows. If your partition was a -system partition than Windows may intentionally reboot after -the successful consistency check. - -.SH OPTIONS -.TP -.B -f -Forces ntfsresize to proceed with the filesystem resize operation, overriding -some safety checks which -.B ntfsresize -normally enforces. You can use this -parameter multiply times if you want to overcome every single safety checks. -.TP -.B -h -Display help and exit. -.TP -.B -i -Using this option you can calculate the smallest shrunken volume size supported. -This option will not make any changes to the filesystem. -.TP -.B -n -You can use this option to make a test run before doing the real resize operation. -Volume will be opened read-only and -.B ntfsresize -displays what it would do if it were to resize the filesystem. -.TP -.B -s \fIsize\fR[\fBk\fR|\fBM\fR|\fBG\fR] -Shrink volume to \fIsize\fR[\fBk\fR|\fBM\fR|\fBG\fR] bytes. -The optional modifiers \fBk\fR, \fBM\fR, \fBG\fR mean the -.I size -parameter is given in kilo-, mega- or gigabytes respectively. -Conforming to standards, k=10^3, M=10^6 and G=10^9. -.SH BUGS -No bugs are known or has been reported so far in the current version. -If you find otherwise, please report it to -(no subscription needed). It's also strongly advised you -.B MAKE SURE YOU HAVE A BACKUP -of your important data in case of an unexpected failure. -.PP -Future work is planned to include support for volume enlargement -and resizing fragmented NTFS volumes. -Please note, Windows 2000, XP and .NET have built in NTFS defragmenter. -.SH AVAILABILITY -.B ntfsresize -is part of the linux-ntfs package and is available from -http://linux-ntfs.sf.net/ as source and pre-compiled binary. -.SH AUTHOR -.B ntfsresize -has been written by -Szabolcs Szakacsits . -.SH ACKNOWLEDGEMENT -Many thanks to Anton Altaparmakov and Richard Russon (FlatCap) -for libntfs, excellent documentation, comments, testing and fixes, -moreover to Theodore Ts'o whose -.BR resize2fs (8) -man page formed the basis of this page. -.SH SEE ALSO -.BR fdisk (8), -.BR cfdisk (8), -.BR sfdisk (8), -.BR parted (8), -.BR mkntfs (8) diff --git a/ntfstools/ntfsresize.c b/ntfstools/ntfsresize.c deleted file mode 100644 index d7d2ab65..00000000 --- a/ntfstools/ntfsresize.c +++ /dev/null @@ -1,914 +0,0 @@ -/** - * ntfsresize - Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Szabolcs Szakacsits - * - * This utility will resize an NTFS volume. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS - * distribution in the file COPYING); if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "types.h" -#include "support.h" -#include "endians.h" -#include "bootsect.h" -#include "disk_io.h" -#include "attrib.h" -#include "volume.h" -#include "mft.h" -#include "bitmap.h" -#include "inode.h" -#include "runlist.h" - -const char *EXEC_NAME = "ntfsresize"; - -static const char *ntfs_report_banner = -"\nReport bugs to linux-ntfs-dev@lists.sf.net. " -"Homepage: http://linux-ntfs.sf.net\n"; - -static const char *resize_warning_msg = -"WARNING: Every sanity check passed and only the DANGEROUS operations left.\n" -"Please make sure all your important data had been backed up in case of an\n" -"unexpected failure!\n"; - -static const char *resize_important_msg = -"NTFS had been successfully resized on device '%s'.\n" -"You can go on to resize the device e.g. with 'fdisk'.\n" -"IMPORTANT: When recreating the partition, make sure you\n" -" 1) create it with the same starting disk cylinder\n" -" 2) create it with the same partition type (usually 7, HPFS/NTFS)\n" -" 3) do not make it smaller than the new NTFS filesystem size\n" -" 4) set the bootable flag for the partition if it existed before\n" -"Otherwise you may lose your data or can't boot your computer from the disk!\n"; - -static const char *fragmented_volume_msg = -"The volume end is fragmented, this case is not yet supported. Defragment it\n" -"(Windows 2000, XP and .NET have built in defragmentation tool) and try again.\n"; - -struct { - int verbose; - int debug; - int ro_flag; - int force; - int info; - s64 bytes; - char *volume; -} opt; - -struct bitmap { - u8 *bm; - s64 size; -}; - -struct progress_bar { - u64 start; - u64 stop; - int resolution; - float unit; -}; - -ntfs_volume *vol = NULL; -struct bitmap lcn_bitmap; - -#define NTFS_MBYTE (1000 * 1000) - -#define ERR_PREFIX "ERROR" -#define PERR_PREFIX ERR_PREFIX "(%d): " -#define NERR_PREFIX ERR_PREFIX ": " - -#define rounded_up_division(a, b) (((a) + (b - 1)) / (b)) - - -void perr_printf(const char *fmt, ...) -{ - va_list ap; - int eo = errno; - - fprintf(stdout, PERR_PREFIX, eo); - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - va_end(ap); - printf(": %s\n", strerror(eo)); - fflush(stdout); - fflush(stderr); -} - - -int err_exit(const char *fmt, ...) -{ - va_list ap; - - fprintf(stdout, NERR_PREFIX); - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - va_end(ap); - fflush(stdout); - fflush(stderr); - exit(1); -} - - -int perr_exit(const char *fmt, ...) -{ - va_list ap; - int eo = errno; - - fprintf(stdout, PERR_PREFIX, eo); - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - va_end(ap); - printf(": %s\n", strerror(eo)); - fflush(stdout); - fflush(stderr); - exit(1); -} - - -void usage() -{ - printf("\n"); - printf ("Usage: %s [-fhin] [-s size[k|M|G]] device\n", EXEC_NAME); - printf("Shrink a defragmented NTFS volume.\n"); - printf("\n"); - Dprintf(" -d Show debug information\n"); - printf (" -f Force to progress (DANGEROUS)\n"); - printf (" -h This help text\n"); - printf (" -i Calculate the smallest shrunken size supported (read-only)\n"); - printf (" -n Make a test run without write operations (read-only)\n"); - printf (" -s size[k|M|G] Shrink volume to size[k|M|G] bytes (k=10^3, M=10^6, G=10^9)\n"); -/* printf (" -v Verbose operation\n"); */ - printf(ntfs_report_banner); - exit(1); -} - - -/* Copy-paste from e2fsprogs */ -void proceed_question(void) -{ - char buf[256]; - const char *short_yes = "yY"; - - fflush(stdout); - fflush(stderr); - printf("Are you sure you want to proceed (y/[n])? "); - buf[0] = 0; - fgets(buf, sizeof(buf), stdin); - if (strchr(short_yes, buf[0]) == 0) { - printf("OK quitting. NO CHANGES has been made to your NTFS volume.\n"); - exit(1); - } -} - - -s64 get_new_volume_size(char *s) -{ - s64 size; - char *suffix; - - size = strtoll(s, &suffix, 10); - if (size <= 0 || errno == ERANGE) - err_exit("Illegal new volume size\n"); - - if (!*suffix) - return size; - - if (strlen(suffix) > 1) - usage(); - - /* We follow the SI prefixes: - http://physics.nist.gov/cuu/Units/prefixes.html - http://physics.nist.gov/cuu/Units/binary.html - Disk partitioning tools use prefixes as, - k M G - old fdisk 2^10 2^20 10^3*2^20 - recent fdisk 10^3 10^6 10^9 - cfdisk 10^3 10^6 10^9 - sfdisk 2^10 2^20 - parted 2^10 2^20 (may change) - fdisk (DOS) 2^10 2^20 - */ - /* FIXME: check for overflow */ - switch (*suffix) { - case 'G': - size *= 1000; - case 'M': - size *= 1000; - case 'k': - size *= 1000; - break; - default: - usage(); - } - - return size; -} - - -void parse_options(int argc, char **argv) -{ - char *s; - int i; - - printf("%s v%s\n", EXEC_NAME, VERSION); - - memset(&opt, 0, sizeof(opt)); - - while ((i = getopt(argc, argv, "dfhins:")) != EOF) - switch (i) { - case 'd': - opt.debug = 1; - break; - case 'f': - opt.force++; - break; - case 'h': - usage(); - case 'i': - opt.info = 1; - break; - case 'n': - opt.ro_flag = MS_RDONLY; - break; - case 's': - opt.bytes = get_new_volume_size(optarg); - break; - case 'v': - opt.verbose++; - break; - default: - usage(); - } - if (optind == argc) - usage(); - opt.volume = argv[optind++]; - if (optind < argc) - usage(); - - stderr = stdout; - if (!opt.debug) - if (!(stderr = fopen("/dev/null", "rw"))) - perr_exit("Couldn't open /dev/null"); - - - /* If no '-s size' then estimate smallest shrunken volume size */ - if (!opt.bytes) - opt.info = 1; - - if (opt.info) { - if (opt.bytes) { - printf(NERR_PREFIX "It makes no sense to use -i and " - "-s together.\n"); - usage(); - } - opt.ro_flag = MS_RDONLY; - } -} - - -s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters) -{ - s64 bm_bsize; - - bm_bsize = rounded_up_division(nr_clusters, 8); - - /* Needs to be multiple of 8 bytes */ - bm_bsize = (bm_bsize + 7) & ~7; - Dprintf("Bitmap byte size : %lld (%lld clusters)\n", - bm_bsize, rounded_up_division(bm_bsize, vol->cluster_size)); - - return bm_bsize; -} - - -void build_lcn_usage_bitmap(ATTR_RECORD *a) -{ - run_list *rl; - int i, j; - - if (!a->non_resident) - return; - - if (!(rl = ntfs_decompress_mapping_pairs(vol, a, NULL))) - perr_exit("ntfs_decompress_mapping_pairs"); - - for (i = 0; rl[i].length; i++) { - if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) - continue; - for (j = 0; j < rl[i].length; j++) { - u64 k = (u64)rl[i].lcn + j; - if (ntfs_get_and_set_bit(lcn_bitmap.bm, k, 1)) - err_exit("Cluster %lu referenced twice!\n" - "You didn't shutdown your Windows" - "properly?", k); - } - } - free(rl); -} - - -void walk_attributes(MFT_RECORD *mr) -{ - ntfs_attr_search_ctx *ctx; - - if (!(ctx = ntfs_get_attr_search_ctx(NULL, mr))) - perr_exit("ntfs_get_attr_search_ctx"); - - while (!ntfs_walk_attrs(ctx)) { - if (ctx->attr->type == AT_END) - break; - build_lcn_usage_bitmap(ctx->attr); - } - - ntfs_put_attr_search_ctx(ctx); -} - - -void get_bitmap_data(ntfs_volume *vol, struct bitmap *bm) -{ - ntfs_inode *ni; - ntfs_attr_search_ctx *ctx; - - if (!(ni = ntfs_open_inode(vol, (MFT_REF)FILE_Bitmap))) - perr_exit("ntfs_open_inode"); - - if (!(ctx = ntfs_get_attr_search_ctx(ni, NULL))) - perr_exit("ntfs_get_attr_search_ctx"); - - if (ntfs_lookup_attr(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) - perr_exit("ntfs_lookup_attr"); - - /* FIXME: get_attribute_value_length() can't handle extents */ - bm->size = get_attribute_value_length(ctx->attr); - - if (!(bm->bm = (u8 *)malloc(bm->size))) - perr_exit("get_bitmap_data"); - - if (get_attribute_value(vol, ni->mrec, ctx->attr, bm->bm) != bm->size) - perr_exit("Couldn't get $Bitmap $DATA\n"); - - ntfs_put_attr_search_ctx(ctx); - ntfs_close_inode(ni); -} - - -void compare_bitmaps(struct bitmap *a, struct bitmap *b) -{ - int i; - - if (a->size != b->size) - err_exit("$Bitmap file size doesn't match " - "calculated size ((%d != %d)\n", a->size, b->size); - - for (i = 0; i < a->size; i++) - if (a->bm[i] != b->bm[i]) - err_exit("Cluster bitmaps differ at %d (%d != %d)\n" - "You didn't shutdown your Windows properly?", - i, a->bm[i], b->bm[i]); -} - - -void progress_init(struct progress_bar *p, u64 start, u64 stop, int res) -{ - p->start = start; - p->stop = stop; - p->unit = 100.0 / (stop - start); - p->resolution = res; -} - - -void progress_update(struct progress_bar *p, u64 current) -{ - float percent = p->unit * current; - - if (current != p->stop) { - if ((current - p->start) % p->resolution) - return; - printf("%6.2f percent completed\r", percent); - } else - printf("100.00 percent completed\n"); - fflush(stdout); -} - -void walk_inodes() -{ - s32 inode = 0; - s64 last_mft_rec; - MFT_REF mref; - MFT_RECORD *mrec = NULL; - struct progress_bar progress; - - printf("Scanning volume ...\n"); - - last_mft_rec = vol->nr_mft_records - 1; - progress_init(&progress, inode, last_mft_rec, 100); - - for (; inode <= last_mft_rec; inode++) { - progress_update(&progress, inode); - - mref = (MFT_REF)inode; - if (ntfs_read_file_record(vol, mref, &mrec, NULL)) { - /* FIXME: continue only if it make sense, e.g. - MFT record not in use based on $MFT bitmap */ - if (errno == EIO) - continue; - perr_exit("Reading inode %ld failed", inode); - } - if (!(mrec->flags & MFT_RECORD_IN_USE)) - continue; - - walk_attributes(mrec); - } - if (mrec) - free(mrec); -} - - -void advise_on_resize() -{ - u64 i, old_b, new_b, g_b, old_mb, new_mb, g_mb; - int fragmanted_end; - - for (i = vol->nr_clusters - 1; i > 0; i--) - if (ntfs_get_bit(lcn_bitmap.bm, i)) - break; - - i += 2; /* first free + we reserve one for the backup boot sector */ - fragmanted_end = (i >= vol->nr_clusters) ? 1 : 0; - - if (fragmanted_end || !opt.info) { - printf(fragmented_volume_msg); - if (fragmanted_end) - exit(1); - printf("Now "); - } - - old_b = vol->nr_clusters * vol->cluster_size; - old_mb = rounded_up_division(old_b, NTFS_MBYTE); - new_b = i * vol->cluster_size; - new_mb = rounded_up_division(new_b, NTFS_MBYTE); - g_b = (vol->nr_clusters - i) * vol->cluster_size; - g_mb = g_b / NTFS_MBYTE; - - printf("You could resize at %lld bytes ", new_b); - - if ((new_mb * NTFS_MBYTE) < old_b) - printf("or %lld MB ", new_mb); - - printf("(freeing "); - - if (g_mb && (old_mb - new_mb)) - printf("%lld MB", old_mb - new_mb); - else - printf("%lld bytes", g_b); - - printf(").\n"); - exit(1); -} - - -void look_for_bad_sector(ATTR_RECORD *a) -{ - run_list *rl; - int i; - - rl = ntfs_decompress_mapping_pairs(vol, a, NULL); - if (!rl) - perr_exit("ntfs_decompress_mapping_pairs"); - - for (i = 0; rl[i].length; i++) - if (rl[i].lcn != LCN_HOLE) - err_exit("Device has bad sectors, not supported\n"); - - free(rl); -} - - -void rl_set(run_list *rl, VCN vcn, LCN lcn, s64 len) -{ - rl->vcn = vcn; - rl->lcn = lcn; - rl->length = len; -} - - -/* - * $Bitmap can overlap the end of the volume. Any bits in this region - * must be set. This region also encompasses the backup boot sector. - */ -void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm) -{ - for (; cluster < bm->size << 3; cluster++) - ntfs_set_bit(bm->bm, (u64)cluster, 1); -} - - -/* - * FIXME: this function should go away and instead using a generalized - * "truncate_bitmap_unnamed_attr()" - */ -void truncate_badclust_bad_attr(ATTR_RECORD *a, s64 nr_clusters) -{ - run_list *rl_bad; - int mp_size; - char *mp; - - if (!a->non_resident) - /* FIXME: handle resident attribute value */ - perr_exit("Resident attribute in $BadClust not supported!"); - - if (!(rl_bad = (run_list *)malloc(2 * sizeof(run_list)))) - perr_exit("Couldn't get memory"); - - rl_set(rl_bad, 0LL, (LCN)LCN_HOLE, nr_clusters); - rl_set(rl_bad + 1, nr_clusters, -1LL, 0LL); - - mp_size = ntfs_get_size_for_mapping_pairs(vol, rl_bad); - - if (!(mp = (char *)calloc(1, mp_size))) - perr_exit("Couldn't get memory"); - - if (ntfs_build_mapping_pairs(vol, mp, mp_size, rl_bad)) - exit(1); - - memcpy((char *)a + a->mapping_pairs_offset, mp, mp_size); - a->highest_vcn = cpu_to_le64(nr_clusters - 1LL); - a->allocated_size = cpu_to_le64(nr_clusters * vol->cluster_size); - a->data_size = cpu_to_le64(nr_clusters * vol->cluster_size); - - free(rl_bad); - free(mp); -} - - -void truncate_bitmap_unnamed_attr(ATTR_RECORD *a, s64 nr_clusters) -{ - run_list *rl; - s64 bm_bsize, size; - s64 nr_bm_clusters; - int i, j, mp_size; - int trunc_at = -1; /* FIXME: -1 means unset */ - char *mp; - - - if (!a->non_resident) - /* FIXME: handle resident attribute value */ - perr_exit("Resident data attribute in $Bitmap not supported!"); - - bm_bsize = nr_clusters_to_bitmap_byte_size(nr_clusters); - nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size); - - if (!(rl = ntfs_decompress_mapping_pairs(vol, a, NULL))) - perr_exit("ntfs_decompress_mapping_pairs"); - - /* Unallocate truncated clusters in $Bitmap */ - for (i = 0; rl[i].length; i++) { - if (rl[i].vcn + rl[i].length <= nr_bm_clusters) - continue; - if (trunc_at == -1) - trunc_at = i; - if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) - continue; - for (j = 0; j < rl[i].length; j++) - if (rl[i].vcn + j >= nr_bm_clusters) { - u64 k = (u64)rl[i].lcn + j; - ntfs_set_bit(lcn_bitmap.bm, k, 0); - Dprintf("Unallocate cluster: " - "%llu (%llx)\n", k, k); - } - } - - /* FIXME: realloc lcn_bitmap.bm (if it's worth the risk) */ - lcn_bitmap.size = bm_bsize; - bitmap_file_data_fixup(nr_clusters, &lcn_bitmap); - - if (trunc_at != -1) { - /* NOTE: 'i' always > 0 */ - i = nr_bm_clusters - rl[trunc_at].vcn; - rl[trunc_at].length = i; - rl_set(rl + trunc_at + 1, nr_bm_clusters, -1LL, 0LL); - - Dprintf("Runlist truncated at index %d, " - "new cluster length %d\n", trunc_at, i); - } - - if (!opt.ro_flag) { - size = ntfs_rl_pwrite(vol, rl, 0, bm_bsize, lcn_bitmap.bm); - if (bm_bsize != size) { - if (size == -1) - perr_exit("Couldn't write $Bitmap"); - printf("Couldn't write full $Bitmap file " - "(%lld from %lld)\n", size, bm_bsize); - exit(1); - } - } - - mp_size = ntfs_get_size_for_mapping_pairs(vol, rl); - - if (!(mp = (char *)calloc(1, mp_size))) - perr_exit("Couldn't get memory"); - - if (ntfs_build_mapping_pairs(vol, mp, mp_size, rl)) - exit(1); - - memcpy((char *)a + a->mapping_pairs_offset, mp, mp_size); - a->highest_vcn = cpu_to_le64(nr_bm_clusters - 1LL); - a->allocated_size = cpu_to_le64(nr_bm_clusters * vol->cluster_size); - a->data_size = cpu_to_le64(bm_bsize); - a->initialized_size = cpu_to_le64(bm_bsize); - - free(rl); - free(mp); -} - - -void lookup_data_attr(MFT_REF mref, char *aname, ntfs_attr_search_ctx **ctx) -{ - ntfs_inode *ni; - uchar_t *ustr = NULL; - int len = 0; - - if (!(ni = ntfs_open_inode(vol, mref))) - perr_exit("ntfs_open_inode"); - - if (NInoAttrList(ni)) - perr_exit("Attribute list attribute not yet supported"); - - if (!(*ctx = ntfs_get_attr_search_ctx(ni, NULL))) - perr_exit("ntfs_get_attr_search_ctx"); - - if (aname && ((len = ntfs_mbstoucs(aname, &ustr, 0)) == -1)) - perr_exit("Unable to convert string to Unicode"); - - if (!ustr || !len) { - ustr = AT_UNNAMED; - len = 0; - } - - if (ntfs_lookup_attr(AT_DATA, ustr, len, 0, 0, NULL, 0, *ctx)) - perr_exit("ntfs_lookup_attr"); - - if (ustr != AT_UNNAMED) - free(ustr); -} - - -int write_mft_record(ntfs_attr_search_ctx *ctx) -{ - if (opt.ro_flag) - return 0; - - return ntfs_write_mft_record(vol, ctx->ntfs_ino->mft_no, ctx->mrec); -} - - -void truncate_badclust_file(s64 nr_clusters) -{ - ntfs_attr_search_ctx *ctx = NULL; - - printf("Updating $BadClust file ...\n"); - - lookup_data_attr((MFT_REF)FILE_BadClus, "$Bad", &ctx); - look_for_bad_sector(ctx->attr); - /* FIXME: sanity_check_attr(ctx->attr); */ - /* FIXME: should use an "extended" truncate_bitmap_unnamed_attr() */ - truncate_badclust_bad_attr(ctx->attr, nr_clusters); - - if (write_mft_record(ctx)) - perr_exit("Couldn't update $BadClust"); - - /* FIXME: clean up API => ntfs_put_attr_search_ctx() also closes ni */ - ntfs_put_attr_search_ctx(ctx); -} - - -void truncate_bitmap_file(s64 nr_clusters) -{ - ntfs_attr_search_ctx *ctx = NULL; - - printf("Updating $Bitmap file ...\n"); - - lookup_data_attr((MFT_REF)FILE_Bitmap, NULL, &ctx); - /* FIXME: sanity_check_attr(ctx->attr); */ - truncate_bitmap_unnamed_attr(ctx->attr, nr_clusters); - - if (write_mft_record(ctx)) - perr_exit("Couldn't update $Bitmap"); - - ntfs_put_attr_search_ctx(ctx); -} - - -void setup_lcn_bitmap() -{ - /* Determine lcn bitmap byte size and allocate it. */ - lcn_bitmap.size = nr_clusters_to_bitmap_byte_size(vol->nr_clusters); - - if (!(lcn_bitmap.bm = (unsigned char *)calloc(1, lcn_bitmap.size))) - perr_exit("Failed to allocate internal buffer"); - - bitmap_file_data_fixup(vol->nr_clusters, &lcn_bitmap); -} - - -/* FIXME: should be done using ntfs_* functions */ -void update_bootsector(s64 nr_clusters) -{ - NTFS_BOOT_SECTOR bs; - - printf("Updating Boot record ...\n"); - - if (lseek(vol->fd, 0, SEEK_SET) == (off_t)-1) - perr_exit("lseek"); - - if (read(vol->fd, &bs, sizeof(NTFS_BOOT_SECTOR)) == -1) - perr_exit("read() error"); - - bs.number_of_sectors = nr_clusters * bs.bpb.sectors_per_cluster; - bs.number_of_sectors = cpu_to_le64(bs.number_of_sectors); - - if (lseek(vol->fd, 0, SEEK_SET) == (off_t)-1) - perr_exit("lseek"); - - if (!opt.ro_flag) - if (write(vol->fd, &bs, sizeof(NTFS_BOOT_SECTOR)) == -1) - perr_exit("write() error"); -} - - -void print_volume_size(char *str, ntfs_volume *v, s64 nr_clusters) -{ - s64 b; /* volume size in bytes */ - - b = nr_clusters * v->cluster_size; - printf("%s: %lld bytes (%lld MB)\n", - str, b, rounded_up_division(b, NTFS_MBYTE)); -} - - -void mount_volume() -{ - unsigned long mntflag; - - if (ntfs_check_if_mounted(opt.volume, &mntflag)) - perr_exit("Failed to check '%s' mount state", opt.volume); - - if (mntflag & NTFS_MF_MOUNTED) { - if (!(mntflag & NTFS_MF_READONLY)) - err_exit("Device %s is mounted read-write. " - "You must 'umount' it first.\n", opt.volume); - if (!opt.ro_flag) - err_exit("Device %s is mounted. " - "You must 'umount' it first.\n", opt.volume); - } - - if (!(vol = ntfs_mount(opt.volume, opt.ro_flag))) { - - int err = errno; - - perr_printf("ntfs_mount failed"); - if (errno == EINVAL) { - printf("Apparently device '%s' doesn't have a " - "valid NTFS. Maybe you selected\nthe whole " - "disk instead of a partition (e.g. /dev/hda, " - "not /dev/hda8)?\n", opt.volume); - } - exit(1); - } - - if (vol->flags & VOLUME_IS_DIRTY) - if (opt.force-- <= 0) - err_exit("Volume is dirty. Run chkdsk and " - "please try again (or see -f option).\n"); - - printf("NTFS volume version: %d.%d\n", vol->major_ver, vol->minor_ver); - if (ntfs_is_version_supported(vol)) - perr_exit("Unknown NTFS version"); - - Dprintf("Cluster size : %u\n", vol->cluster_size); - print_volume_size("Current volume size", vol, vol->nr_clusters); -} - - -void prepare_volume_fixup() -{ - if (!opt.ro_flag) { - u16 flags; - - flags = vol->flags | VOLUME_IS_DIRTY; - if (vol->major_ver >= 2) - flags |= VOLUME_MOUNTED_ON_NT4; - - printf("Schedule chkdsk NTFS consistency check at Windows boot time ...\n"); - if (ntfs_set_volume_flags(vol, flags)) - perr_exit("Failed to set $Volume dirty"); - - printf("Resetting $LogFile ... " - "(this might take a while)\n"); - if (ntfs_reset_logfile(vol)) - perr_exit("Failed to reset $LogFile"); - } -} - - -int main(int argc, char **argv) -{ - struct bitmap on_disk_lcn_bitmap; - s64 new_volume_size = 0; /* in clusters */ - int i; - - parse_options(argc, argv); - - mount_volume(); - - if (opt.bytes) { - /* Take the integer part: when shrinking we don't want - to make the volume to be bigger than requested. - Later on we will also decrease this value to save - room for the backup boot sector */ - new_volume_size = opt.bytes / vol->cluster_size; - print_volume_size("New volume size ", vol, new_volume_size); - } - - setup_lcn_bitmap(); - - walk_inodes(); - - get_bitmap_data(vol, &on_disk_lcn_bitmap); - compare_bitmaps(&on_disk_lcn_bitmap, &lcn_bitmap); - free(on_disk_lcn_bitmap.bm); - - if (opt.info) - advise_on_resize(); - - /* FIXME: check new_volume_size validity */ - - /* Backup boot sector at the end of device isn't counted in NTFS - volume size thus we have to reserve space for. We don't trust - the user does this for us: better to be on the safe side ;) */ - if (new_volume_size) - --new_volume_size; - - if (new_volume_size > vol->nr_clusters) - err_exit("Volume enlargement not yet supported\n"); - else if (new_volume_size == vol->nr_clusters) { - printf("Nothing to do: NTFS volume size is already OK.\n"); - exit(0); - } - - for (i = new_volume_size; i < vol->nr_clusters; i++) - if (ntfs_get_bit(lcn_bitmap.bm, (u64)i)) { - /* FIXME: relocate cluster */ - advise_on_resize(); - } - - if (opt.force-- <= 0 && !opt.ro_flag) { - printf(resize_warning_msg); - proceed_question(); - } - - prepare_volume_fixup(); - - truncate_badclust_file(new_volume_size); - truncate_bitmap_file(new_volume_size); - update_bootsector(new_volume_size); - - /* We don't create backup boot sector because we don't know where the - partition will be split. The scheduled chkdsk will fix it anyway */ - - if (opt.ro_flag) { - printf("The read-only test run ended successfully.\n"); - exit(0); - } - - printf("Syncing device ...\n"); - if (fsync(vol->fd) == -1) - perr_exit("fsync"); - - printf(resize_important_msg, vol->dev_name); - return 0; -} - diff --git a/ntfstools/ntfsundelete.8.in b/ntfstools/ntfsundelete.8.in deleted file mode 100644 index f28f4a05..00000000 --- a/ntfstools/ntfsundelete.8.in +++ /dev/null @@ -1,344 +0,0 @@ -.\" Copyright (c) 2002 Richard Russon. All Rights Reserved. -.\" This file may be copied under the terms of the GNU Public License. -.\" -.TH NTFSUNDELETE 8 "June 2002" "Linux\-NTFS version @VERSION@" -.SH NAME -ntfsundelete \- recover a deleted file from an NTFS volume. -.SH SYNOPSIS -.B ntfsundelete -[ -.I options -] -.B device -.SH DESCRIPTION -.B ntfsundelete -has three modes of operation: -.IR scan , -.I undelete -and -.IR copy . -.SS Scan -.PP -The default mode, -.I scan -simply reads an NTFS Volume and looks for files that have been deleted. Then it -will print a list giving the inode number, name and size. -.SS Undelete -.PP -The -.I undelete -mode takes the inode and recovers as much of the data as possible. It save the -result to another location. Partly for safety, but mostly because NTFS write -support isn't finished. -.SS Copy -.PP -This is a wizard's option. It will save a portion of the MFT to a file. This -probably only be useful when debugging -.I ntfsundelete -.SS Notes -.B ntfsundelete -only ever -.B reads -from the NTFS Volume. -.B ntfsundelete -will never change the volume. -.SH CAVEATS -.SS Miracles -.B ntfsundelete -cannot perform the impossible. -.PP -When a file is deleted the MFT Record is marked as not in use and the bitmap -representing the disk usage is updated. If the power isn't turned off -immediately, the free space, where the file used to live, may become -overwritten. Worse, the MFT Record may be reused for another file. If this -happens it is impossible to tell where the file was on disk. -.PP -Even if all the clusters of a file are not in use, there is no guarantee that -they haven't been overwritten by some short\-lived file. -.SS Locale -In NTFS all the filenames are stored as Unicode. They will be converted into -the current locale for display by -.BR ntfsundelete . -The utility has successfully displayed some Chinese pictogram filenames and then -correctly recovered them. -.SS Extended MFT Records -In rare circumstances, a single MFT Record will not be large enough to hold the -metadata describing a file (a file would have to be in hundreds of fragments -for this to happen). In these cases one MFT record may hold the filename, but -another will hold the information about the data. -.B ntfsundelete -will not try and piece together such records. It will simply show unnamed files -with data. -.SS Compressed and Encrypted Files -.B ntfsundelete -cannot recover compressed or encrypted files. When scanning for them, it will -display as being 0% recoverable. -.SH OPTIONS -Below is a summary of all the options that -.B ntfsundelete -accepts. All options have two equivalent names. The short name is preceded by -.BR \- -and the long name is preceded by -.BR \-\- . -Any single letter options, that don't take an argument, can be combined into a -single command, e.g. -.BR \-fv -is equivalent to -.BR "\-f \-v" . -Long named options can be abbreviated to any unique prefix of their name. -.TP -.BI "\-b " num -.br -.ns -.TP -.BI "\-\-byte " num -If any clusters of the file cannot be recovered, the missing parts will be -filled with this byte. The default is zeros. -.TP -.B \-C -.br -.ns -.TP -.B \-\-case -When scanning an NTFS volume, any filename matching (using the -.B \-\-match -option) is case\-insensitive. This option makes the maching case\-sensitive. -.TP -.BI "\-c " range -.br -.ns -.TP -.BI "\-\-copy " range -This wizard's option will write a block of MFT FILE records to a file. The -default file is -.I mft -which will be created in the current directory. This option can be combined -with the -.B \-\-output -and -.B \-\-destination -options. -.TP -.BI "\-d " dir -.br -.ns -.TP -.BI "\-\-destination " dir -This option controls where to put the output file of the -.B \-\-undelete -and -.B \-\-copy -options. -.TP -.B \-f -.br -.ns -.TP -.B \-\-force -This will override some sensible defaults, such as not overwriting an existing -file. Use this option with caution. -.TP -.B \-h -.br -.ns -.TP -.B \-\-help -Show a list of options with a brief description of each one. -.TP -.BI "\-m " pattern -.br -.ns -.TP -.BI "\-\-match " pattern -Filter the output of the -.B \-\-scan -option, by only looking for matching filenames. The pattern can include the -wildcards '?', match exactly one character or '*', match zero or more -characters. By default the matching is case\-insensitive. To make the search -case sensitive, use the -.B \-\-case -option. -.TP -.BI "\-o " file -.br -.ns -.TP -.BI "\-\-output " file -Use this option to set name of output file that -.B \-\-undelete -or -.B \-\-copy -will create. -.TP -.BI "\-p " num -.br -.ns -.TP -.BI "\-\-percentage " num -Filter the output of the -.B \-\-scan -option, by only matching files with a certain amount of recoverable content. -.B Please read the caveats section for more details. -.TP -.BI \-q -.br -.ns -.TP -.BI \-\-quiet -Reduce the amount of output to a minimum. Naturally, it doesn't make sense to -combine this option with -.BR \-\-scan . -.TP -.B \-s -.br -.ns -.TP -.B \-\-scan -Search through an NTFS volume and print a list of files that could be recovered. -This is the default action of -.BR ntfsundelete . -This list can be filtered by filename, size, percentage recoverable or last -modification time, using the -.BR \-\-match , -.BR \-\-size , -.B \-\-percent -and -.B \-\-time -options, respectively. -.sp -The output of scan will be: -.sp -.nf -Inode Flags %age Date Size Filename - 6038 FN.. 93% 2002-07-17 26629 thesis.doc -.fi -.TS -lB lB -l l. -Flag Description -F/D File/Directory -N/R (Non-)Resident data stream -C/E Compressed/Encrypted data stream -! Missing attributes -.TE -.RS -.sp -.br -The percentage field shows how much of the file can potentially be recovered. -.sp -.br -.RE -.BI "\-S " range -.br -.ns -.TP -.BI "\-\-size " range -Filter the output of the -.B \-\-scan -option, by looking for a particular range of file sizes. The range may be -specified as two numbers separated by a '\-'. The sizes may be abbreviated -using the suffixes k, m, g, t, for kilobytes, megabytes, gigabytes and terabytes -respectively. -.TP -.BI "\-t " since -.br -.ns -.TP -.BI "\-\-time " since -Filter the output of the -.B \-\-scan -option. Only match files that have been altered since this time. The time must -be given as number using a suffix of d, w, m, y for days, weeks, months or years -ago. -.TP -.BI "\-u " num -.br -.ns -.TP -.BI "\-\-undelete " num -Recover the file with this inode number. This option can be combined with -.BR \-\-output , -.BR \-\-destination , -and -.BR \-\-byte . -.TP -.B \-v -.br -.ns -.TP -.B \-\-verbose -Increase the amount of output that -.B ntfsundelete -prints. -.TP -.B \-V -.br -.ns -.TP -.B \-\-version -Show the version number, copyright and license -.BR ntfsundelete . -.SH EXAMPLES -Look for deleted files on /dev/hda1. -.RS -.sp -.B ntfsundelete /dev/hda1 -.sp -.RE -Look for deleted documents on /dev/hda1. -.RS -.sp -.B ntfsundelete /dev/hda1 -s \-m '*.doc' -.sp -.RE -Look for deleted files between 5000 and 6000000 bytes, with at least 90% of the -data recoverable, on /dev/hda1. -.RS -.sp -.B ntfsundelete /dev/hda1 \-S 5k\-6m \-p 90 -.sp -.RE -Look for deleted files altered in the last two days -.RS -.sp -.B ntfsundelete /dev/hda1 \-t 2d -.sp -.RE -Undelete inode number 3689, call the file 'work.doc' and put it in the user's -home directory. -.RS -.sp -.B ntfsundelete /dev/hda1 \-u 3689 \-o work.doc \-d ~ -.sp -.RE -Save MFT Records 3689 to 3690 to a file 'debug' -.RS -.sp -.B ntfsundelete /dev/hda1 \-c 3689\-3690 \-o debug -.RE -.SH BUGS -There are some small limitations to this program, but currently no known bugs. -If you find one, please send an email to -.nh - -.hy -.SH AUTHOR -.B ntfsundelete -was written by Richard Russon (FlatCap) -.br -If you find this tool useful, make FlatCap happy and send him an email. -.SH AVAILABILITY -.B ntfsundelete -is part of the linux\-ntfs package and is available from -.br -.nh -http://linux\-ntfs.sourceforge.net/downloads.html -.hy -This manual page is available online at: -.br -.nh -http://linux\-ntfs.sourceforge.net/tools/ntfsundelete.html -.hy -.SH SEE ALSO -.BR ntfsinfo(8) -.br diff --git a/ntfstools/ntfsundelete.c b/ntfstools/ntfsundelete.c deleted file mode 100644 index f738f3bf..00000000 --- a/ntfstools/ntfsundelete.c +++ /dev/null @@ -1,2018 +0,0 @@ -/** - * ntfsundelete - Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Richard Russon - * - * This utility will recover deleted files from an NTFS volume. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS - * distribution in the file COPYING); if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ntfsundelete.h" -#include "bootsect.h" -#include "mft.h" -#include "attrib.h" -#include "layout.h" -#include "inode.h" -#include "disk_io.h" - -static const char *AUTHOR = "Richard Russon (FlatCap)"; -static const char *EXEC_NAME = "ntfsundelete"; -static const char *MFTFILE = "mft"; -static const char *UNNAMED = ""; -static char *NONE = ""; -static char *UNKNOWN = "unknown"; -static struct options opts; - -#define _(S) gettext(S) - -/** - * Eprintf - Print error messages - */ -void Eprintf (const char *format, ...) -{ - va_list va; - va_start (va, format); - vfprintf (stderr, format, va); - va_end (va); -} - -/** - * Iprintf - Print informative messages - */ -void Iprintf (const char *format, ...) -{ - va_list va; -#ifndef DEBUG - if (opts.quiet) - return; -#endif - va_start (va, format); - vfprintf (stdout, format, va); - va_end (va); -} - -/** - * Vprintf - Print verbose messages - */ -void Vprintf (const char *format, ...) -{ - va_list va; -#ifndef DEBUG - if (!opts.verbose) - return; -#endif - va_start (va, format); - vfprintf (stdout, format, va); - va_end (va); -} - -/** - * Dprintf - Print debug messages - */ -void Dprintf (const char *format, ...) -{ -#ifdef DEBUG - va_list va; - va_start (va, format); - vfprintf (stdout, format, va); - va_end (va); -#endif -} - - -/** - * version - Print version information about the program - * - * Print a copyright statement and a brief description of the program. - * - * Return: none - */ -void version (void) -{ - Iprintf ("%s v%s Copyright (C) 2002 %s\nRecover deleted files from an " - "NTFS Volume\n\n%s is free software, released under the GNU " - "General Public License\nand you are welcome to redistribute " - "it under certain conditions.\n%s comes with ABSOLUTELY NO " - "WARRANTY; for details read the GNU\nGeneral Public License " - "to be found in the file COPYING in the main\nLinux-NTFS " - "distribution directory.\n\n", - EXEC_NAME, VERSION, AUTHOR, EXEC_NAME, EXEC_NAME); -} - -/** - * usage - Print a list of the parameters to the program - * - * Print a list of the parameters and options for the program. - * - * Return: none - */ -void usage (void) -{ - Iprintf ("Usage: %s [options] device\n" - " -s --scan Scan for files (default)\n" - " -p num --percentage num Minimum percentage recoverable\n" - " -m pattern --match pattern Only work on files with matching names\n" - " -C --case Case sensitive matching\n" - " -S range --size range Match files of this size\n" - " -t since --time since Last referenced since this time\n" - "\n" - " -u num --undelete num Undelete inode\n" - " -o file --output file Save with this filename\n" - " -d dir --destination dir Destination directory\n" - " -b num --byte num Fill missing parts with this byte\n" - "\n" - " -c range --copy range Write a range of MFT records to a file\n" - "\n" - " -f --force Use less caution\n" - " -q --quiet Less output\n" - " -v --verbose More output\n" - " -V --version Version information\n" - " -h --help Print this help\n\n", - EXEC_NAME); - Iprintf ("Please report bugs to: linux-ntfs-dev@lists.sf.net\n\n"); -} - -/** - * transform - Convert a shell style pattern to a regex - * @pattern: String to be converted - * @regex: Resulting regular expression is put here - * - * This will transform patterns, such as "*.doc" to true regular expressions. - * The function will also place '^' and '$' around the expression to make it - * behave as the user would expect - * - * Before After - * . \. - * * .* - * ? . - * - * Notes: - * The returned string must be freed by the caller. - * If transform fails, @regex will not be changed. - * - * Return: 1, Success, the string was transformed - * 0, An error occurred - */ -int transform (const char *pattern, char **regex) -{ - char *result; - int length, i, j; - - if (!pattern || !regex) - return 0; - - length = strlen (pattern); - if (length < 1) { - Eprintf ("Pattern to transform is empty\n"); - return 0; - } - - for (i = 0; pattern[i]; i++) { - if ((pattern[i] == '*') || (pattern[i] == '.')) - length++; - } - - result = malloc (length + 3); - if (!result) { - Eprintf ("Couldn't allocate memory in transform()\n"); - return 0; - } - - result[0] = '^'; - - for (i = 0, j = 1; pattern[i]; i++, j++) { - if (pattern[i] == '*') { - result[j] = '.'; - j++; - result[j] = '*'; - } else if (pattern[i] == '.') { - result[j] = '\\'; - j++; - result[j] = '.'; - } else if (pattern[i] == '?') { - result[j] = '.'; - } else { - result[j] = pattern[i]; - } - } - - result[j] = '$'; - result[j+1] = 0; - Dprintf ("Pattern '%s' replaced with regex '%s'\n", pattern, result); - - *regex = result; - return 1; -} - -/** - * parse_time - Convert a time abbreviation to seconds - * @string: The string to be converted - * @since: The absolute time referred to - * - * Strings representing times will be converted into a time_t. The numbers will - * be regarded as seconds unless suffixed. - * - * Suffix Description - * [yY] Year - * [mM] Month - * [wW] Week - * [dD] Day - * [sS] Second - * - * Therefore, passing "1W" will return the time_t representing 1 week ago. - * - * Notes: - * Only the first character of the suffix is read. - * If parse_time fails, @since will not be changed - * - * Return: 1 Success - * 0 Error, the string was malformed - */ -int parse_time (const char *value, time_t *since) -{ - time_t result, now; - char *suffix = NULL; - - if (!value || !since) - return -1; - - Dprintf ("parsing time '%s' ago\n", value); - - result = strtoll (value, &suffix, 10); - if (result < 0 || errno == ERANGE) { - Eprintf ("Invalid time '%s'.\n", value); - return 0; - } - - if (!suffix) { - Eprintf ("Internal error, strtoll didn't return a suffix.\n"); - return 0; - } - - if (strlen (suffix) > 1) { - Eprintf ("Invalid time suffix '%s'. Use Y, M, W, D or H.\n", suffix); - return 0; - } - - switch (suffix[0]) { - case 'y': case 'Y': result *= 12; - case 'm': case 'M': result *= 4; - case 'w': case 'W': result *= 7; - case 'd': case 'D': result *= 24; - case 'h': case 'H': result *= 3600; - case 0: - break; - - default: - Eprintf ("Invalid time suffix '%s'. Use Y, M, W, D or H.\n", suffix); - return 0; - } - - now = time (NULL); - - Dprintf ("Time now = %lld, Time then = %lld.\n", (long long) now, (long long) result); - *since = now - result; - return 1; -} - -/** - * parse_size - Convert a string representing a size - * @value: String to be parsed - * @size: Parsed size - * - * Read a string and convert it to a number. Strings may be suffixed to scale - * them. Any number without a suffix is assumed to be in bytes. - * - * Suffix Description Multiple - * [tT] Terabytes 10^12 - * [gG] Gigabytes 10^9 - * [mM] Megabytes 10^6 - * [kK] Kilobytes 10^3 - * - * Notes: - * Only the first character of the suffix is read. - * The multipliers are decimal thousands, not binary: 1000, not 1024. - * If parse_size fails, @size will not be changed - * - * Return: 1 Success - * 0 Error, the string was malformed - */ -int parse_size (const char *value, long long *size) -{ - long long result; - char *suffix = NULL; - - if (!value || !size) - return 0; - - Dprintf ("Parsing size '%s'.\n", value); - - result = strtoll (value, &suffix, 10); - if (result < 0 || errno == ERANGE) { - Eprintf ("Invalid size '%s'.\n", value); - return 0; - } - - if (!suffix) { - Eprintf ("Internal error, strtoll didn't return a suffix.\n"); - return 0; - } - - - /*if (strlen (suffix) > 1) { - Eprintf ("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix); - return 0; - } Can't do this because of ranges*/ - - switch (suffix[0]) { - case 't': case 'T': result *= 1000; - case 'g': case 'G': result *= 1000; - case 'm': case 'M': result *= 1000; - case 'k': case 'K': result *= 1000; - case '-': case 0: - break; - default: - Eprintf ("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix); - return 0; - } - - Dprintf ("Parsed size = %lld.\n", result); - *size = result; - return 1; -} - -/** - * parse_range - Convert a string representing a range of numbers - * @string: The string to be parsed - * @start: The beginning of the range will be stored here - * @finish: The end of the range will be stored here - * - * Read a string of the form n-m. If the lower end is missing, zero will be - * substituted. If the upper end is missing LONG_MAX will be used. If the - * string cannot be parsed correctly, @start and @finish will not be changed. - * - * Return: 1 Success, a valid string was found - * 0 Error, the string was not a valid range - */ -int parse_range (const char *string, long long *start, long long *finish) -{ - long long a, b; - char *middle; - - if (!string || !start || !finish) - return 0; - - middle = strchr (string, '-'); - if (string == middle) { - Dprintf ("Range has no beginning, defaulting to 0.\n"); - a = 0; - } else { - if (!parse_size (string, &a)) - return 0; - } - - if (middle) { - if (middle[1] == 0) { - b = LONG_MAX; - Dprintf ("Range has no end, defaulting to %lld.\n", b); - } else { - if (!parse_size (middle+1, &b)) - return 0; - } - } else { - b = a; - } - - Dprintf ("Range '%s' = %lld - %lld\n", string, a, b); - - *start = a; - *finish = b; - return 1; -} - -/** - * parse_options - Read and validate the programs command line - * - * Read the command line, verify the syntax and parse the options. - * This function is very long, but quite simple. - * - * Return: 1 Success - * 0 Error, one or more problems - */ -int parse_options (int argc, char *argv[]) -{ - static const char *sopt = "-b:Cc:d:fhm:o:p:sS:t:u:qvV"; - static const struct option lopt[] = { - { "byte", required_argument, NULL, 'b' }, - { "case", no_argument, NULL, 'C' }, - { "copy", required_argument, NULL, 'c' }, - { "destination", required_argument, NULL, 'd' }, - { "force", no_argument, NULL, 'f' }, - { "help", no_argument, NULL, 'h' }, - { "match", required_argument, NULL, 'm' }, - { "output", required_argument, NULL, 'o' }, - { "percentage", required_argument, NULL, 'p' }, - { "scan", no_argument, NULL, 's' }, - { "size", required_argument, NULL, 'S' }, - { "time", required_argument, NULL, 't' }, - { "undelete", required_argument, NULL, 'u' }, - { "quiet", no_argument, NULL, 'q' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - char c = -1; - char *end = NULL; - int err = 0; - int ver = 0; - int help = 0; - - opterr = 0; /* We'll handle the errors, thank you. */ - - opts.mode = MODE_NONE; - opts.uinode = -1; - opts.percent = -1; - opts.fillbyte = -1; - - while ((c = getopt_long (argc, argv, sopt, lopt, NULL)) != -1) { - switch (c) { - case 1: /* A non-option argument */ - if (!opts.device) { - opts.device = argv[optind-1]; - } else { - opts.device = NULL; - err++; - } - break; - case 'b': - if (opts.fillbyte == -1) { - end = NULL; - opts.fillbyte = strtol (optarg, &end, 0); - if (end && *end) - err++; - } else { - err++; - } - break; - case 'C': - opts.match_case++; - break; - case 'c': - if (opts.mode == MODE_NONE) { - if (!parse_range (argv[optind-1], &opts.mft_begin, &opts.mft_end)) - err++; - opts.mode = MODE_COPY; - } else { - opts.mode = MODE_ERROR; - } - break; - case 'd': - if (!opts.dest) - opts.dest = argv[optind-1]; - else - err++; - break; - case 'f': - opts.force++; - break; - case 'h': - help++; - break; - case 'm': - if (!opts.match) { - if (!transform (argv[optind-1], &opts.match)) - err++; - } else { - err++; - } - break; - case 'o': - if (!opts.output) { - opts.output = argv[optind-1]; - } else { - err++; - } - break; - case 'p': - if (opts.percent == -1) { - end = NULL; - opts.percent = strtol (optarg, &end, 0); - if (end && ((*end != '%') && (*end != 0))) - err++; - } else { - err++; - } - break; - case 'q': - opts.quiet++; - break; - case 's': - if (opts.mode == MODE_NONE) - opts.mode = MODE_SCAN; - else - opts.mode = MODE_ERROR; - break; - case 'S': - if ((opts.size_begin > 0) || (opts.size_end > 0) || - !parse_range (argv[optind-1], &opts.size_begin, - &opts.size_end)) { - err++; - } - break; - case 't': - if (opts.since == 0) { - if (!parse_time (argv[optind-1], &opts.since)) - err++; - } else { - err++; - } - break; - case 'u': - if (opts.mode == MODE_NONE) { - end = NULL; - opts.mode = MODE_UNDELETE; - opts.uinode = strtol (optarg, &end, 0); - if (end && *end) - err++; - } else { - opts.mode = MODE_ERROR; - } - break; - case 'v': - opts.verbose++; - break; - case 'V': - ver++; - break; - default: - if (((optopt == 'b') || (optopt == 'c') || - (optopt == 'd') || (optopt == 'm') || - (optopt == 'o') || (optopt == 'p') || - (optopt == 'S') || (optopt == 't') || - (optopt == 'u')) && (!optarg)) { - Eprintf ("Option '%s' requires an argument.\n", argv[optind-1]); - } else { - Eprintf ("Unknown option '%s'.\n", argv[optind-1]); - } - err++; - break; - } - } - - if (help || ver) { - opts.quiet = 0; - } else { - if (opts.device == NULL) { - Eprintf ("You must specify exactly one device.\n"); - err++; - } - - if (opts.mode == MODE_NONE) { - opts.mode = MODE_SCAN; - } - - switch (opts.mode) { - case MODE_SCAN: - if (opts.output || opts.dest || (opts.fillbyte != -1)) { - Eprintf ("Scan can only be used with --percent, " - "--match, --ignore-case, --size and --time.\n"); - err++; - } - if (opts.match_case && !opts.match) { - Eprintf ("The --case option doesn't make sense without the --match option\n"); - err++; - } - break; - case MODE_UNDELETE: - if ((opts.percent != -1) || opts.match || opts.match_case || - (opts.size_begin > 0) || (opts.size_end > 0)) { - Eprintf ("Undelete can only be used with " - "--output, --destination and --byte.\n"); - err++; - } - break; - case MODE_COPY: - if ((opts.fillbyte != -1) || (opts.percent != -1) || - opts.match || opts.match_case || - (opts.size_begin > 0) || (opts.size_end > 0)) { - Eprintf ("Copy can only be used with --output and --destination.\n"); - err++; - } - break; - default: - Eprintf ("You can only select one of Scan, Undelete or Copy.\n"); - err++; - } - - if ((opts.percent < -1) || (opts.percent > 100)) { - Eprintf ("Percentage value must be in the range 0 - 100.\n"); - err++; - } - - if (opts.quiet) { - if (opts.verbose) { - Eprintf ("You may not use --quiet and --verbose at the same time.\n"); - err++; - } else if (opts.mode == MODE_SCAN) { - Eprintf ("You may not use --quiet when scanning a volume.\n"); - err++; - } - } - } - - if (ver) - version(); - if (help || err) - usage(); - - return (!err && !help && !ver); -} - - -/** - * free_file - Release the resources used by a file object - * @file: The unwanted file object - * - * This will free up the memory used by a file object and iterate through the - * object's children, freeing their resources too. - * - * Return: none - */ -void free_file (struct ufile *file) -{ - struct list_head *item, *tmp; - - if (!file) - return; - - list_for_each_safe (item, tmp, &file->name) { /* List of filenames */ - struct filename *f = list_entry (item, struct filename, list); - Dprintf ("freeing filename '%s'\n", f->name ? f->name : NONE); - if (f->name) - free (f->name); - free (f); - } - - list_for_each_safe (item, tmp, &file->data) { /* List of data streams */ - struct data *d = list_entry (item, struct data, list); - Dprintf ("freeing data stream '%s'\n", d->name ? d->name : UNNAMED); - if (d->name) - free (d->name); - if (d->run_list) - free (d->run_list); - free (d); - } - - free (file->mft); - free (file); -} - -/** - * ntfs2utc - Convert an NTFS time to Unix time - * @time: An NTFS time in 100ns units since 1601 - * - * NTFS stores times as the number of 100ns intervals since January 1st 1601 at - * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD. - * - * Return: n A Unix time (number of seconds since 1970) - */ -time_t ntfs2utc (long long time) -{ - return (time - ((long long) (369 * 365 + 89) * 24 * 3600 * 10000000)) / 10000000; -} - -/** - * find_attribute - Find an attribute of the given type - * @type: An attribute type, e.g. AT_FILE_NAME - * @ctx: A search context, created using ntfs_get_attr_search_ctx - * - * Using the search context to keep track, find the first/next occurrence of a - * given attribute type. - * - * N.B. This will return a pointer into @mft. As long as the search context - * has been created without an inode, it won't overflow the buffer. - * - * Return: Pointer Success, an attribute was found - * NULL Error, no matching attributes were found - */ -ATTR_RECORD * find_attribute (const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) -{ - if (!ctx) - return NULL; - - if (ntfs_lookup_attr (type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) { - Dprintf ("find_attribute didn't find an attribute of type: 0x%02x.\n", type); - return NULL; /* None / no more of that type */ - } - - Dprintf ("find_attribute found an attribute of type: 0x%02x.\n", type); - return ctx->attr; -} - -/** - * find_first_attribute - Find the first attribute of a given type - * @type: An attribute type, e.g. AT_FILE_NAME - * @mft: A buffer containing a raw MFT record - * - * Search through a raw MFT record for an attribute of a given type. - * The return value is a pointer into the MFT record that was supplied. - * - * N.B. This will return a pointer into @mft. The pointer won't stray outside - * the buffer, since we created the search context without an inode. - * - * Return: Pointer Success, an attribute was found - * NULL Error, no matching attributes were found - */ -ATTR_RECORD * find_first_attribute (const ATTR_TYPES type, MFT_RECORD *mft) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *rec; - - if (!mft) - return NULL; - - ctx = ntfs_get_attr_search_ctx (NULL, mft); - if (!ctx) { - Eprintf ("Couldn't create a search context.\n"); - return NULL; - } - - rec = find_attribute (type, ctx); - ntfs_put_attr_search_ctx (ctx); - if (rec) - Dprintf ("find_first_attribute: found attr of type 0x%02x.\n", type); - else - Dprintf ("find_first_attribute: didn't find attr of type 0x%02x.\n", type); - return rec; -} - -/** - * get_filenames - Read an MFT Record's $FILENAME attributes - * @file: The file object to work with - * - * A single file may have more than one filename. This is quite common. - * Windows creates a short DOS name for each long name, e.g. LONGFI~1.XYZ, - * LongFiLeName.xyZ. - * - * The filenames that are found are put in filename objects and added to a - * linked list of filenames in the file object. For convenience, the unicode - * filename is converted into the current locale and stored in the filename - * object. - * - * One of the filenames is picked (the one with the lowest numbered namespace) - * and its locale friendly name is put in pref_name. - * - * Return: n The number of $FILENAME attributes found - * -1 Error - */ -int get_filenames (struct ufile *file) -{ - ATTR_RECORD *rec; - FILE_NAME_ATTR *attr; - ntfs_attr_search_ctx *ctx; - struct filename *name; - int count = 0; - int space = 4; - - if (!file) - return -1; - - ctx = ntfs_get_attr_search_ctx (NULL, file->mft); - if (!ctx) - return -1; - - while ((rec = find_attribute (AT_FILE_NAME, ctx))) { - /* We know this will always be resident. */ - attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu (rec->value_offset)); - - name = calloc (1, sizeof (*name)); - if (!name) { - Eprintf ("Couldn't allocate memory in get_filenames().\n"); - count = -1; - break; - } - - name->uname = attr->file_name; - name->uname_len = attr->file_name_length; - name->name_space = attr->file_name_type; - name->size_alloc = sle64_to_cpu (attr->allocated_size); - name->size_data = sle64_to_cpu (attr->data_size); - name->flags = attr->file_attributes; - - name->date_c = ntfs2utc (sle64_to_cpu (attr->creation_time)); - name->date_a = ntfs2utc (sle64_to_cpu (attr->last_data_change_time)); - name->date_m = ntfs2utc (sle64_to_cpu (attr->last_mft_change_time)); - name->date_r = ntfs2utc (sle64_to_cpu (attr->last_access_time)); - - file->date = max (file->date, name->date_c); - file->date = max (file->date, name->date_a); - file->date = max (file->date, name->date_m); - file->date = max (file->date, name->date_r); - - if (ntfs_ucstombs (name->uname, name->uname_len, &name->name, - name->uname_len) < 0) { - Dprintf ("Couldn't translate filename to current locale.\n"); - } - - if (name->name_space < space) { - file->pref_name = name->name; - space = name->name_space; - } - - file->max_size = max (file->max_size, name->size_alloc); - file->max_size = max (file->max_size, name->size_data); - - list_add_tail (&name->list, &file->name); - count++; - } - - ntfs_put_attr_search_ctx (ctx); - Dprintf ("File has %d names.\n", count); - return count; -} - -/** - * get_data - Read an MFT Record's $DATA attributes - * @file: The file object to work with - * @vol: An ntfs volume obtained from ntfs_mount - * - * A file may have more than one data stream. All files will have an unnamed - * data stream which contains the file's data. Some Windows applications store - * extra information in a separate stream. - * - * The streams that are found are put in data objects and added to a linked - * list of data streams in the file object. - * - * Return: n The number of $FILENAME attributes found - * -1 Error - */ -int get_data (struct ufile *file, ntfs_volume *vol) -{ - ATTR_RECORD *rec; - ntfs_attr_search_ctx *ctx; - int count = 0; - struct data *data; - - if (!file) - return -1; - - ctx = ntfs_get_attr_search_ctx (NULL, file->mft); - if (!ctx) - return -1; - - while ((rec = find_attribute (AT_DATA, ctx))) { - data = calloc (1, sizeof (*data)); - if (!data) { - Eprintf ("Couldn't allocate memory in get_data().\n"); - count = -1; - break; - } - - data->resident = !rec->non_resident; - data->compressed = rec->flags & ATTR_IS_COMPRESSED; - data->encrypted = rec->flags & ATTR_IS_ENCRYPTED; - - if (rec->name_length) { - data->uname = (uchar_t *) ((char *) rec + le16_to_cpu (rec->name_offset)); - data->uname_len = rec->name_length; - - if (ntfs_ucstombs (data->uname, data->uname_len, &data->name, - data->uname_len) < 0) { - Eprintf ("Cannot translate name into current locale.\n"); - } - } - - if (data->resident) { - data->size_data = le32_to_cpu (rec->value_length); - data->data = ((char*) (rec)) + le16_to_cpu (rec->value_offset); - } else { - data->size_alloc = sle64_to_cpu (rec->allocated_size); - data->size_data = sle64_to_cpu (rec->data_size); - data->size_init = sle64_to_cpu (rec->initialized_size); - data->size_vcn = sle64_to_cpu (rec->highest_vcn) + 1; - } - - data->run_list = ntfs_decompress_mapping_pairs (vol, rec, NULL); - if (!data->run_list) { - Dprintf ("Couldn't decompress the data runs\n"); - } - - file->max_size = max (file->max_size, data->size_data); - file->max_size = max (file->max_size, data->size_init); - - list_add_tail (&data->list, &file->data); - count++; - } - - ntfs_put_attr_search_ctx (ctx); - Dprintf ("File has %d data streams.\n", count); - return count; -} - -/** - * read_record - Read an MFT record into memory - * @vol: An ntfs volume obtained from ntfs_mount - * @record: The record number to read - * - * Read the specified MFT record and gather as much information about it as - * possible. - * - * Return: Pointer A ufile object containing the results - * NULL Error - */ -struct ufile * read_record (ntfs_volume *vol, long long record) -{ - ATTR_RECORD *attr10, *attr20, *attr90; - struct ufile *file; - ntfs_attr *mft; - - if (!vol) - return NULL; - - file = calloc (1, sizeof (*file)); - if (!file) { - Eprintf ("Couldn't allocate memory in read_record()\n"); - return NULL; - } - - INIT_LIST_HEAD (&file->name); - INIT_LIST_HEAD (&file->data); - file->inode = record; - - file->mft = malloc (vol->mft_record_size); - if (!file->mft) { - Eprintf ("Couldn't allocate memory in read_record()\n"); - free_file (file); - return NULL; - } - - mft = ntfs_attr_open (vol->mft_ni, AT_DATA, NULL, 0); - if (!mft) { - Eprintf ("Couldn't open $MFT/$DATA: %s\n", strerror (errno)); - free_file (file); - return NULL; - } - - if (ntfs_attr_mst_pread (mft, vol->mft_record_size * record, 1, vol->mft_record_size, file->mft) < 1) { - Eprintf ("Couldn't read MFT Record %lld.\n", record); - ntfs_attr_close (mft); - free_file (file); - return NULL; - } - - ntfs_attr_close (mft); - mft = NULL; - - attr10 = find_first_attribute (AT_STANDARD_INFORMATION, file->mft); - attr20 = find_first_attribute (AT_ATTRIBUTE_LIST, file->mft); - attr90 = find_first_attribute (AT_INDEX_ROOT, file->mft); - - Dprintf ("Attributes present: %s %s %s\n", attr10?"0x10":"", attr20?"0x20":"", attr90?"0x90":""); - - if (attr10) - { - STANDARD_INFORMATION *si; - si = (STANDARD_INFORMATION *) ((char *) attr10 + le16_to_cpu (attr10->value_offset)); - file->date = max (file->date, ntfs2utc (sle64_to_cpu (si->last_data_change_time))); - } - - if (attr20 || !attr10) - file->attr_list = 1; - if (attr90) - file->directory = 1; - - if (get_filenames (file) < 0) { - Eprintf ("Couldn't get filenames.\n"); - } - if (get_data (file, vol) < 0) { - Eprintf ("Couldn't get data streams.\n"); - } - - return file; -} - - -/** - * cluster_in_use - Determine if a cluster is in use - * @vol: An ntfs volume obtained from ntfs_mount - * @lcn: The Logical Cluster Number to test - * - * The metadata file $Bitmap has one binary bit representing each cluster on - * disk. The bit will be set of each cluster that is in use. The function - * reads the relevant part of $Bitmap into a buffer and tests the bit. - * - * This function has a static buffer in which it caches a section of $Bitmap. - * If the lcn, being tested, lies outside the range, the buffer will be - * refreshed. - * - * Return: 1 Cluster is in use - * 0 Cluster is free space - * -1 Error occurred - */ -int cluster_in_use (ntfs_volume *vol, long long lcn) -{ - static unsigned char buffer[512]; - static long long bmplcn = -sizeof (buffer) - 1; /* Which bit of $Bitmap is in the buffer */ - - int byte, bit; - ntfs_attr *attr; - - if (!vol) - return -1; - - /* Does lcn lie in the section of $Bitmap we already have cached? */ - if ((lcn < bmplcn) || (lcn >= (bmplcn + (sizeof (buffer) << 3)))) { - Dprintf ("Bit lies outside cache.\n"); - attr = ntfs_attr_open (vol->lcnbmp_ni, AT_DATA, NULL, 0); - if (!attr) { - Eprintf ("Couldn't open $MFT/$BITMAP: %s\n", strerror (errno)); - return -1; - } - - /* Mark the buffer as in use, in case the read is shorter. */ - memset (buffer, 0xFF, sizeof (buffer)); - bmplcn = lcn & (~((sizeof (buffer) << 3) - 1)); - - if (ntfs_attr_pread (attr, (bmplcn>>3), sizeof (buffer), buffer) < 0) { - Eprintf ("Couldn't read $MFT/$BITMAP: %s\n", strerror (errno)); - ntfs_attr_close (attr); - return -1; - } - - Dprintf ("Reloaded bitmap buffer.\n"); - ntfs_attr_close (attr); - } - - bit = 1 << (lcn & 7); - byte = (lcn >> 3) & (sizeof (buffer) - 1); - Dprintf ("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, in use %d\n", - lcn, bmplcn, byte, bit, buffer[byte] & bit); - - return (buffer[byte] & bit); -} - -/** - * calc_percentage - Calculate how much of the file is recoverable - * @file: The file object to work with - * @vol: An ntfs volume obtained from ntfs_mount - * - * Read through all the $DATA streams and determine if each cluster in each - * stream is still free disk space. This is just measuring the potential for - * recovery. The data may have still been overwritten by a another file which - * was then deleted. - * - * Files with a resident $DATA stream will have a 100% potential. - * - * N.B. If $DATA attribute spans more than one MFT record (i.e. badly - * fragmented) then only the data in this segment will be used for the - * calculation. - * - * N.B. Currently, compressed and encrypted files cannot be recovered, so they - * will return 0%. - * - * Return: n The percentage of the file that _could_ be recovered - * -1 Error - */ -int calc_percentage (struct ufile *file, ntfs_volume *vol) -{ - run_list_element *rl = NULL; - struct list_head *pos; - struct data *data; - long long i, j; - long long start, end; - int inuse, free; - int percent = 0; - - if (!file || !vol) - return -1; - - if (file->directory) { - Dprintf ("Found a directory: not recoverable.\n"); - return 0; - } - - if (list_empty (&file->data)) { - Vprintf ("File has no data streams.\n"); - return 0; - } - - list_for_each (pos, &file->data) { - data = list_entry (pos, struct data, list); - inuse = 0; - free = 0; - - if (data->encrypted) { - Vprintf ("File is encrypted, recovery is impossible.\n"); - continue; - } - - if (data->compressed) { - Vprintf ("File is compressed, recovery not yet implemented.\n"); - continue; - } - - if (data->resident) { - Vprintf ("File is resident, therefore recoverable.\n"); - percent = 100; - data->percent = 100; - continue; - } - - rl = data->run_list; - if (!rl) { - Vprintf ("File has no run list, hence no data.\n"); - continue; - } - - if (rl[0].length <= 0) { - Vprintf ("File has an empty run list, hence no data.\n"); - continue; - } - - if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */ - Vprintf ("Missing segment at beginning, %lld clusters\n", rl[0].length); - inuse += rl[0].length; - rl++; - } - - for (i = 0; rl[i].length > 0; i++) { - if (rl[i].lcn == LCN_RL_NOT_MAPPED) { - Vprintf ("Missing segment at end, %lld clusters\n", rl[i].length); - inuse += rl[i].length; - continue; - } - - if (rl[i].lcn == LCN_HOLE) { - free += rl[i].length; - continue; - } - - start = rl[i].lcn; - end = rl[i].lcn + rl[i].length; - - for (j = start; j < end; j++) { - if (cluster_in_use (vol, j)) - inuse++; - else - free++; - } - } - - if ((inuse + free) == 0) { - Eprintf ("Unexpected error whilst calculating percentage for inode %lld\n", file->inode); - continue; - } - - data->percent = (free * 100) / (inuse + free); - - percent = max (percent, data->percent); - } - - Vprintf ("File is %d%% recoverable\n", percent); - return percent; -} - -/** - * dump_record - Print everything we know about an MFT record - * @file: The file to work with - * - * Output the contents of the file object. This will print everything that has - * been read from the MFT record, or implied by various means. - * - * Because of the redundant nature of NTFS, there will be some duplication of - * information, though it will have been read from different sources. - * - * N.B. If the filename is missing, or couldn't be converted to the current - * locale, "" will be displayed. - * - * Return: none - */ -void dump_record (struct ufile *file) -{ - char buffer[20]; - char *name; - struct list_head *item; - int i; - - if (!file) - return; - - Iprintf ("MFT Record %lld\n", file->inode); - Iprintf ("Type: %s\n", (file->directory) ? "Directory" : "File"); - strftime (buffer, sizeof (buffer), "%F %R", localtime (&file->date)); - Iprintf ("Date: %s\n", buffer); - - if (file->attr_list) - Iprintf ("Metadata may span more than one MFT record\n"); - - list_for_each (item, &file->name) { - struct filename *f = list_entry (item, struct filename, list); - - if (f->name) - name = f->name; - else - name = NONE; - - Iprintf ("Filename: (%d) %s\n", f->name_space, f->name); - Iprintf ("File Flags: "); - if (f->flags & FILE_ATTR_SYSTEM) Iprintf ("System "); - if (f->flags & FILE_ATTR_DIRECTORY) Iprintf ("Directory "); - if (f->flags & FILE_ATTR_SPARSE_FILE) Iprintf ("Sparse "); - if (f->flags & FILE_ATTR_REPARSE_POINT) Iprintf ("Reparse "); - if (f->flags & FILE_ATTR_COMPRESSED) Iprintf ("Compressed "); - if (f->flags & FILE_ATTR_ENCRYPTED) Iprintf ("Encrypted "); - if (!(f->flags & (FILE_ATTR_SYSTEM || FILE_ATTR_DIRECTORY || - FILE_ATTR_SPARSE_FILE || FILE_ATTR_REPARSE_POINT || - FILE_ATTR_COMPRESSED || FILE_ATTR_ENCRYPTED))) { - Iprintf (NONE); - } - Iprintf ("\n"); - Iprintf ("Size alloc: %lld\n", f->size_alloc); - Iprintf ("Size data: %lld\n", f->size_data); - - strftime (buffer, sizeof (buffer), "%F %R", localtime (&f->date_c)); - Iprintf ("Date C: %s\n", buffer); - strftime (buffer, sizeof (buffer), "%F %R", localtime (&f->date_a)); - Iprintf ("Date A: %s\n", buffer); - strftime (buffer, sizeof (buffer), "%F %R", localtime (&f->date_m)); - Iprintf ("Date M: %s\n", buffer); - strftime (buffer, sizeof (buffer), "%F %R", localtime (&f->date_r)); - Iprintf ("Date R: %s\n", buffer); - } - - Iprintf ("Data Streams:\n"); - list_for_each (item, &file->data) { - struct data *d = list_entry (item, struct data, list); - Iprintf ("Name: %s\n", (d->name) ? d->name : ""); - Iprintf ("Flags: "); - if (d->resident) Iprintf ("Resident\n"); - if (d->compressed) Iprintf ("Compressed\n"); - if (d->encrypted) Iprintf ("Encrypted\n"); - if (!d->resident && !d->compressed && !d->encrypted) - Iprintf ("None\n"); - else - Iprintf ("\n"); - - Iprintf ("Size alloc: %lld\n", d->size_alloc); - Iprintf ("Size data: %lld\n", d->size_data); - Iprintf ("Size init: %lld\n", d->size_init); - Iprintf ("Size vcn: %lld\n", d->size_vcn); - - Iprintf ("Data runs:\n"); - if ((!d->run_list) || (d->run_list[0].length <= 0)) { - Iprintf (" None\n"); - } else { - for (i = 0; d->run_list[i].length > 0; i++) { - Iprintf (" %lld @ %lld\n", d->run_list[i].length, d->run_list[i].lcn); - } - } - - Iprintf ("Amount potentially recoverable %d%%\n", d->percent); - } - - Iprintf ("________________________________________\n\n"); -} - -/** - * list_record - Print a one line summary of the file - * @file: The file to work with - * - * Print a one line description of a file. - * - * Inode Flags %age Date Size Filename - * - * The output will contain the file's inode number (MFT Record), some flags, - * the percentage of the file that is recoverable, the last modification date, - * the size and the filename. - * - * The flags are F/D = File/Directory, N/R = Data is (Non-)Resident, - * C = Compressed, E = Encrypted, ! = Metadata may span multiple records. - * - * N.B. The file size is stored in many forms in several attributes. This - * display the largest it finds. - * - * N.B. If the filename is missing, or couldn't be converted to the current - * locale, "" will be displayed. - * - * Return: none - */ -void list_record (struct ufile *file) -{ - char buffer[20]; - struct list_head *item; - char *name = NULL; - long long size = 0; - int percent = 0; - - char flagd = '.', flagr = '.', flagc = '.', flagx = '.'; - - strftime (buffer, sizeof (buffer), "%F", localtime (&file->date)); - - if (file->attr_list) - flagx = '!'; - - if (file->directory) - flagd = 'D'; - else - flagd = 'F'; - - list_for_each (item, &file->data) { - struct data *d = list_entry (item, struct data, list); - - if (!d->name) { - if (d->resident) flagr = 'R'; - else flagr = 'N'; - if (d->compressed) flagc = 'C'; /* These two are mutually exclusive */ - if (d->encrypted) flagc = 'E'; - - percent = max (percent, d->percent); - } - - size = max (size, d->size_data); - size = max (size, d->size_init); - } - - if (file->pref_name) - name = file->pref_name; - else - name = NONE; - - Iprintf ("%-8lld %c%c%c%c %3d%% %s %9lld %s\n", - file->inode, flagd, flagr, flagc, flagx, - percent, buffer, size, name); -} - -/** - * name_match - Does a file have a name matching a regex - * @re: The regular expression object - * @file: The file to be tested - * - * Iterate through the file's $FILENAME attributes and compare them against the - * regular expression, created with regcomp. - * - * Return: 1 There is a matching filename. - * 0 There is no match. - */ -int name_match (regex_t *re, struct ufile *file) -{ - struct list_head *item; - int result; - - if (!re || !file) - return 0; - - list_for_each (item, &file->name) { - struct filename *f = list_entry (item, struct filename, list); - - if (!f->name) - continue; - result = regexec (re, f->name, 0, NULL, 0); - if (result < 0) { - Eprintf ("Couldn't compare filename with regex: %s\n", strerror (errno)); - return 0; - } else if (result == REG_NOERROR) { - Dprintf ("Found a matching filename.\n"); - return 1; - } - } - - Dprintf ("Filename '%s' doesn't match regex.\n", file->pref_name); - return 0; -} - -/** - * write_data - Write out a block of data - * @fd: File descriptor to write to - * @buffer: Data to write - * @bufsize: Amount of data to write - * - * Write a block of data to a file descriptor. - * - * Return: -1 Error, something went wrong - * 0 Success, all the data was written - */ -unsigned int write_data (int fd, const char *buffer, unsigned int bufsize) -{ - ssize_t result1, result2; - - if (!buffer) { - errno = EINVAL; - return -1; - } - - result1 = write (fd, buffer, bufsize); - if ((result1 == (ssize_t) bufsize) || (result1 < 0)) - return result1; - - /* Try again with the rest of the buffer */ - buffer += result1; - bufsize -= result1; - - result2 = write (fd, buffer, bufsize); - if (result2 < 0) - return result1; - - return result1 + result2; -} - -/** - * open_file - Create a file based on the dir, name and stream supplied - * @dir: Directory in which to create the file (optional) - * @name: Filename to give the file (optional) - * @stream: Name of the stream (optional) - * - * Create a file and return the file descriptor. All the components are - * optional. If the name is missing, "unknown" will be used. If the directory - * is missing the file will be created in the current directory. If the stream - * name is present it will be appended to the filename, delimited by a colon. - * - * Return: -1 Error, failed to create the file - * n Success, this is the file descriptor - */ -int open_file (const char *dir, const char *name, const char *stream) -{ - char buf[256]; - int flags; - - if (!name) - name = UNKNOWN; - - if (dir) - if (stream) - snprintf (buf, sizeof (buf), "%s/%s:%s", dir, name, stream); - else - snprintf (buf, sizeof (buf), "%s/%s", dir, name); - else - if (stream) - snprintf (buf, sizeof (buf), "%s:%s", name, stream); - else - snprintf (buf, sizeof (buf), "%s", name); - - Vprintf ("Creating file: %s\n", buf); - - if (opts.force) - flags = O_RDWR | O_CREAT | O_TRUNC; - else - flags = O_RDWR | O_CREAT | O_EXCL; - - return open (buf, flags, S_IRUSR | S_IWUSR); -} - - -/** - * scan_disk - Search an NTFS volume for files that could be undeleted - * @vol: An ntfs volume obtained from ntfs_mount - * - * Read through all the MFT entries looking for deleted files. For each one - * determine how much of the data lies in unused disk space. - * - * The list can be filtered by name, size and date, using command line options. - * - * Return: -1 Error, something went wrong - * n Success, the number of recoverable files - */ -int scan_disk (ntfs_volume *vol) -{ - const int BUFSIZE = 8192; - char *buffer = NULL; - int results = 0; - ntfs_attr *attr; - long long size; - long long read; - long long bmpsize; - int i, j, k, b; - int percent; - struct ufile *file; - regex_t re; - - if (!vol) - return -1; - - attr = ntfs_attr_open (vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); - if (!attr) { - Eprintf ("Couldn't open $MFT/$BITMAP: %s\n", strerror (errno)); - return -1; - } - bmpsize = attr->initialized_size; - - buffer = malloc (BUFSIZE); - if (!buffer) { - Eprintf ("Couldn't allocate memory in scan_disk()\n"); - results = -1; - goto out; - } - - if (opts.match) { - int flags = REG_NOSUB; - - if (!opts.match_case) - flags |= REG_ICASE; - if (regcomp (&re, opts.match, flags)) { - Eprintf ("Couldn't create a regex.\n"); - goto out; - } - } - - Iprintf ("Inode Flags %%age Date Size Filename\n"); - Iprintf ("---------------------------------------------------------------\n"); - for (i = 0; i < bmpsize; i += BUFSIZE) { - read = min ((bmpsize - i), BUFSIZE); - size = ntfs_attr_pread (attr, i, read, buffer); - if (size < 0) - break; - - for (j = 0; j < size; j++) { - b = buffer[j]; - for (k = 0; k < 8; k++, b>>=1) { - if (((i+j)*8+k) >= vol->nr_mft_records) - goto done; - if (b & 1) - continue; - file = read_record (vol, (i+j)*8+k); - if (!file) { - Eprintf ("Couldn't read MFT Record %d.\n", (i+j)*8+k); - continue; - } - - if ((opts.since > 0) && (file->date <= opts.since)) - goto skip; - if (opts.match && !name_match (&re, file)) - goto skip; - if (opts.size_begin && (opts.size_begin > file->max_size)) - goto skip; - if (opts.size_end && (opts.size_end < file->max_size)) - goto skip; - - percent = calc_percentage (file, vol); - - if ((opts.percent == -1) || (percent >= opts.percent)) { - if (opts.verbose) - dump_record (file); - else - list_record (file); - } - - if (((opts.percent == -1) && (percent > 0)) || - ((opts.percent > 0) && (percent >= opts.percent))) { - results++; - } -skip: - free_file (file); - } - } - } -done: - Iprintf ("\nFiles with potentially recoverable content: %d\n", results); -out: - if (opts.match) - regfree (&re); - free (buffer); - if (attr) - ntfs_attr_close (attr); - return results; -} - -/** - * undelete_file - Recover a deleted file from an NTFS volume - * @vol: An ntfs volume obtained from ntfs_mount - * @inode: MFT Record number to be recovered - * - * Read an MFT Record and try an recover any data associated with it. Some of - * the clusters may be in use; these will be filled with zeros or the fill byte - * supplied in the options. - * - * Each data stream will be recovered and saved to a file. The file's name will - * be the original filename and it will be written to the current directory. - * Any named data stream will be saved as filename:streamname. - * - * The output file's name and location can be altered by using the command line - * options. - * - * N.B. We cannot tell if someone has overwritten some of the data since the - * file was deleted. - * - * Return: 0 Error, something went wrong - * 1 Success, the data was recovered - */ -int undelete_file (ntfs_volume *vol, long long inode) -{ - char *buffer = NULL; - struct ufile *file; - int i, j; - long long start, end; - run_list_element *rl; - struct list_head *item; - int fd = -1; - long long k; - int result = 0; - - if (!vol) - return 0; - - file = read_record (vol, inode); - if (!file || !file->mft) { - Eprintf ("Can't read info from mft record %lld.\n", inode); - return 0; - } - - buffer = malloc (vol->mft_record_size); - if (!buffer) - goto free; - - if (opts.verbose) { - dump_record (file); - } else { - Iprintf ("Inode Flags %%age Date Size Filename\n"); - Iprintf ("---------------------------------------------------------------\n"); - list_record (file); - Iprintf ("\n"); - } - - if (file->mft->flags & MFT_RECORD_IN_USE) { - Eprintf ("Record is in use by the mft\n"); - if (!opts.force) { - free_file (file); - return 0; - } - Vprintf ("Forced to continue.\n"); - } - - if (calc_percentage (file, vol) == 0) { - Iprintf ("File has no recoverable data.\n"); - goto free; - } - - if (list_empty (&file->data)) { - Iprintf ("File has no data. There is nothing to recover.\n"); - goto free; - } - - list_for_each (item, &file->data) { - struct data *d = list_entry (item, struct data, list); - - if (d->resident) { - fd = open_file (opts.dest, file->pref_name, d->name); - if (fd < 0) { - Eprintf ("Couldn't create file: %s\n", strerror (errno)); - goto free; - } - - Vprintf ("File has resident data.\n"); - if (write_data (fd, d->data, d->size_data) < d->size_data) { - Eprintf ("Write failed: %s\n", strerror (errno)); - close (fd); - goto free; - } - - if (close (fd) < 0) { - Eprintf ("Close failed: %s\n", strerror (errno)); - } - fd = -1; - } else { - rl = d->run_list; - if (!rl) { - Vprintf ("File has no run list, hence no data.\n"); - continue; - } - - if (rl[0].length <= 0) { - Vprintf ("File has an empty run list, hence no data.\n"); - continue; - } - - fd = open_file (opts.dest, file->pref_name, d->name); - if (fd < 0) { - Eprintf ("Couldn't create output file: %s\n", strerror (errno)); - goto free; - } - - if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */ - Vprintf ("Missing segment at beginning, %lld clusters.\n", rl[0].length); - memset (buffer, opts.fillbyte, sizeof (buffer)); - for (k = 0; k < rl[0].length * vol->cluster_size; k += sizeof (buffer)) { - if (write_data (fd, buffer, sizeof (buffer)) < sizeof (buffer)) { - Eprintf ("Write failed: %s\n", strerror (errno)); - close (fd); - goto free; - } - } - } - - for (i = 0; rl[i].length > 0; i++) { - - if (rl[i].lcn == LCN_RL_NOT_MAPPED) { - Vprintf ("Missing segment at end, %lld clusters.\n", rl[i].length); - memset (buffer, opts.fillbyte, sizeof (buffer)); - for (k = 0; k < rl[k].length * vol->cluster_size; k += sizeof (buffer)) { - if (write_data (fd, buffer, sizeof (buffer)) < sizeof (buffer)) { - Eprintf ("Write failed: %s\n", strerror (errno)); - close (fd); - goto free; - } - } - continue; - } - - if (rl[i].lcn == LCN_HOLE) { - Vprintf ("File has a sparse section.\n"); - memset (buffer, 0, sizeof (buffer)); - for (k = 0; k < rl[k].length * vol->cluster_size; k += sizeof (buffer)) { - if (write_data (fd, buffer, sizeof (buffer)) < sizeof (buffer)) { - Eprintf ("Write failed: %s\n", strerror (errno)); - close (fd); - goto free; - } - } - continue; - } - - start = rl[i].lcn; - end = rl[i].lcn + rl[i].length; - - for (j = start; j < end; j++) { - if (cluster_in_use (vol, j)) { - memset (buffer, opts.fillbyte, sizeof (buffer)); - if (write_data (fd, buffer, sizeof (buffer)) < sizeof (buffer)) { - Eprintf ("Write failed: %s\n", strerror (errno)); - close (fd); - goto free; - } - } else { - if (ntfs_read_clusters (vol, j, 1, buffer) < 1) { - Eprintf ("Read failed: %s\n", strerror (errno)); - close (fd); - goto free; - } - if (write_data (fd, buffer, sizeof (buffer)) < sizeof (buffer)) { - Eprintf ("Write failed: %s\n", strerror (errno)); - close (fd); - goto free; - } - } - } - } - Iprintf ("\n"); - if (close (fd) < 0) { - Eprintf ("Close failed: %s\n", strerror (errno)); - } - fd = -1; - - } - if (d->name) - Iprintf ("Undeleted '%s:%s' successfully.\n", file->pref_name, d->name); - else - Iprintf ("Undeleted '%s' successfully.\n", file->pref_name); - } - result = 1; -free: - if (buffer) - free (buffer); - free_file (file); - return result; -} - -/** - * copy_mft - Write a range of MFT Records to a file - * @vol: An ntfs volume obtained from ntfs_mount - * @mft_begin: First MFT Record to save - * @mft_end: Last MFT Record to save - * - * Read a number of MFT Records and write them to a file. - * - * Return: 0 Success, all the records were written - * 1 Error, something went wrong - */ -int copy_mft (ntfs_volume *vol, long long mft_begin, long long mft_end) -{ - ntfs_attr *mft; - char *buffer; - const char *name; - long long i; - int result = 1; - int fd; - - if (!vol) - return 1; - - if (mft_end < mft_begin) { - Eprintf ("Range to copy is backwards.\n"); - return 1; - } - - buffer = malloc (vol->mft_record_size); - if (!buffer) { - Eprintf ("Couldn't allocate memory in copy_mft()\n"); - return 1; - } - - mft = ntfs_attr_open (vol->mft_ni, AT_DATA, NULL, 0); - if (!mft) { - Eprintf ("Couldn't open $MFT/$DATA: %s\n", strerror (errno)); - goto free; - } - - name = opts.output; - if (!name) { - name = MFTFILE; - Dprintf ("No output filename, defaulting to '%s'.\n", name); - } - - fd = open_file (opts.dest, name, NULL); - if (fd < 0) { - Eprintf ("Couldn't open output file '%s': %s\n", name, strerror (errno)); - goto attr; - } - - mft_end = min (mft_end, vol->nr_mft_records - 1); - - Dprintf ("MFT records\n"); - Dprintf (" Total: %8lld\n", vol->nr_mft_records); - Dprintf (" Begin: %8lld\n", mft_begin); - Dprintf (" End: %8lld\n", mft_end); - - for (i = mft_begin; i <= mft_end; i++) { - if (ntfs_attr_pread (mft, vol->mft_record_size * i, vol->mft_record_size, buffer) < vol->mft_record_size) { - Eprintf ("Couldn't read MFT Record %d: %s.\n", i, strerror (errno)); - goto close; - } - - if (write_data (fd, buffer, vol->mft_record_size) < vol->mft_record_size) { - Eprintf ("Write failed: %s\n", strerror (errno)); - goto close; - } - } - - Vprintf ("Read %d MFT Records\n", mft_end - mft_begin + 1); - result = 0; -close: - close (fd); -attr: - ntfs_attr_close (mft); -free: - free (buffer); - return result; -} - -/** - * valid_device - Perform some safety checks on the device, before we start - * @name: Full pathname of the device/file to work with - * @force: Continue regardless of problems - * - * Check that the name refers to a device and that is isn't already mounted. - * These checks can be overridden by using the force option. - * - * Return: 1 Success, we can continue - * 0 Error, we cannot use this device - */ -int valid_device (const char *name, int force) -{ - unsigned long mnt_flags = 0; - struct stat st; - - if (stat (name, &st) == -1) { - if (errno == ENOENT) { - Eprintf ("The device %s doesn't exist\n", name); - } else { - Eprintf ("Error getting information about %s: %s\n", name, strerror (errno)); - } - return 0; - } - - if (!S_ISBLK (st.st_mode)) { - Vprintf ("%s is not a block device.\n", name); - if (!force) { - Eprintf ("Use the force option to work with files.\n"); - return 0; - } - Vprintf ("Forced to continue.\n"); - } - - /* Make sure the file system is not mounted. */ - if (ntfs_check_if_mounted (name, &mnt_flags)) { - Vprintf ("Failed to determine whether %s is mounted: %s\n", name, strerror (errno)); - if (!force) { - Eprintf ("Use the force option to ignore this error.\n"); - return 0; - } - Vprintf ("Forced to continue.\n"); - } else if (mnt_flags & NTFS_MF_MOUNTED) { - Vprintf ("The device %s, is mounted.\n", name); - if (!force) { - Eprintf ("Use the force option to work a mounted filesystem.\n"); - return 0; - } - Vprintf ("Forced to continue.\n"); - } - - Dprintf ("Device %s, will be used\n", name); - return 1; -} - -/** - * main - Begin here - * - * Start from here. - * - * Return: 0 Success, the program worked - * 1 Error, something went wrong - */ -int main (int argc, char *argv[]) -{ - const char *locale; - ntfs_volume *vol; - int result = 1; - - locale = setlocale (LC_ALL, ""); - if (!locale) { - locale = setlocale (LC_ALL, NULL); - Vprintf ("Failed to set locale, using default '%s'.\n", locale); - } else { - Vprintf ("Using locale '%s'.\n", locale); - } - - if (!parse_options (argc, argv)) - goto free; - - if (!valid_device (opts.device, opts.force)) - goto free; - - vol = ntfs_mount (opts.device, MS_RDONLY); - if (!vol) { - Eprintf ("Couldn't mount device '%s': %s\n", opts.device, strerror (errno)); - goto free; - } - - if (vol->flags & VOLUME_IS_DIRTY) { - Iprintf ("Volume is dirty.\n"); - if (!opts.force) { - Eprintf ("Run chkdsk and try again, or use the --force option.\n"); - goto umount; - } - Iprintf ("Forced to continue.\n"); - } - - switch (opts.mode) { - case MODE_SCAN: - result = !scan_disk (vol); - if (result) - Vprintf ("Failed to scan device '%s'.\n", opts.device); - break; - case MODE_UNDELETE: - result = !undelete_file (vol, opts.uinode); - if (result) - Vprintf ("Failed to undelete inode %d.\n", opts.uinode); - break; - case MODE_COPY: - result = !copy_mft (vol, opts.mft_begin, opts.mft_end); - if (result) - Vprintf ("Failed to read MFT blocks %lld-%lld.\n", - opts.mft_begin, min (vol->nr_mft_records, opts.mft_end)); - break; - default: - ; /* Cannot happen */ - } - -umount: - ntfs_umount (vol, FALSE); -free: - if (opts.match) - free (opts.match); - - return result; -} - diff --git a/ntfstools/ntfsundelete.h b/ntfstools/ntfsundelete.h deleted file mode 100644 index 21a63d8b..00000000 --- a/ntfstools/ntfsundelete.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * ntfsundelete - Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Richard Russon - * - * This utility will recover deleted files from an NTFS volume. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS - * distribution in the file COPYING); if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _NTFSUNDELETE_H_ -#define _NTFSUNDELETE_H_ - -#include "types.h" -#include "list.h" -#include "runlist.h" - -enum optmode { - MODE_NONE = 0, - MODE_SCAN, - MODE_UNDELETE, - MODE_COPY, - MODE_ERROR -}; - -struct options { - char *device; /* Device/File to work with */ - enum optmode mode; /* Scan / Undelete / Copy */ - int percent; /* Minimum recoverability */ - int uinode; /* Undelete this inode */ - char *dest; /* Save file to this directory */ - char *output; /* With this filename */ - char fillbyte; /* Use for unrecoverable sections */ - char *match; /* Pattern for filename matching */ - int match_case; /* Case sensitive matching */ - int quiet; /* Less output */ - int verbose; /* Extra output */ - int force; /* Override common sense */ - time_t since; /* Since this time */ - long long size_begin; /* Range for file size */ - long long size_end; - long long mft_begin; /* Range for mft copy */ - long long mft_end; -}; - -struct filename { - struct list_head list; /* Previous/Next links */ - char *name; /* Filename in current locale */ - FILE_NAME_TYPE_FLAGS name_space; - uchar_t *uname; /* Filename in unicode */ - int uname_len; /* and its length */ - long long size_alloc; /* Allocated size (multiple of cluster size) */ - long long size_data; /* Actual size of data */ - FILE_ATTR_FLAGS flags; - time_t date_c; - time_t date_a; - time_t date_m; - time_t date_r; -}; - -struct data { - struct list_head list; /* Previous/Next links */ - char *name; /* Stream name in current locale */ - uchar_t *uname; /* Unicode stream name */ - int uname_len; /* and its length */ - int resident; /* Stream is resident */ - int compressed; /* Stream is compressed */ - int encrypted; /* Stream is encrypted */ - long long size_alloc; /* Allocated size (multiple of cluster size) */ - long long size_data; /* Actual size of data */ - long long size_init; /* Initialised size, may be less than data size */ - long long size_vcn; /* Highest VCN in the data runs */ - run_list_element*run_list; /* Decoded data runs */ - int percent; /* Amont potentially recoverable */ - void *data; /* If resident, a pointer to the data */ -}; - -struct ufile { - long long inode; /* MFT record number */ - time_t date; /* Last modification date/time */ - struct list_head name; /* A list of filenames */ - struct list_head data; /* A list of data streams */ - char *pref_name; /* Preferred filename */ - long long max_size; /* Largest size we find */ - int attr_list; /* MFT record may be one of many */ - int directory; /* MFT record represents a directory */ - MFT_RECORD *mft; /* Raw MFT record */ -}; - -#endif /* _NTFSUNDELETE_H_ */ - diff --git a/ntfstools/ntfswipe.c b/ntfstools/ntfswipe.c deleted file mode 100644 index 120e6e52..00000000 --- a/ntfstools/ntfswipe.c +++ /dev/null @@ -1,732 +0,0 @@ -/** - * ntfswipe - Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Richard Russon - * - * This utility will overwrite usused space on an NTFS volume. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS - * distribution in the file COPYING); if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ntfswipe.h" -#include "types.h" -#include "volume.h" - -static const char *AUTHOR = "Richard Russon (FlatCap)"; -static const char *EXEC_NAME = "ntfswipe"; -static struct options opts; - -/** - * Eprintf - Print error messages - */ -void Eprintf (const char *format, ...) -{ - va_list va; - va_start (va, format); - vfprintf (stderr, format, va); - va_end (va); -} - -/** - * Iprintf - Print informative messages - */ -void Iprintf (const char *format, ...) -{ - va_list va; -#ifndef DEBUG - if (opts.quiet) - return; -#endif - va_start (va, format); - vfprintf (stdout, format, va); - va_end (va); -} - -/** - * Vprintf - Print verbose messages - */ -void Vprintf (const char *format, ...) -{ - va_list va; -#ifndef DEBUG - if (!opts.verbose) - return; -#endif - va_start (va, format); - vfprintf (stdout, format, va); - va_end (va); -} - -/** - * Dprintf - Print debug messages - */ -#ifndef DEBUG -#define Dprintf(...) -#else -void Dprintf (const char *format, ...) -{ - va_list va; - va_start (va, format); - vfprintf (stdout, format, va); - va_end (va); -} -#endif - -/** - * wipe_unused - Wipe unused clusters - * @vol: An ntfs volume obtained from ntfs_mount - * @byte: Overwrite with this value - * - * Read $Bitmap and wipe any clusters that are marked as not in use. - * - * Return: 1 Success, the clusters were wiped - * 0 Error, something went wrong - */ -int wipe_unused (ntfs_volume *vol, int byte) -{ - if (!vol || (byte < 0)) - return 0; - - Iprintf ("wipe_unused 0x%02x\n", byte); - return 1; -} - -/** - * wipe_tails - Wipe the file tails - * @vol: An ntfs volume obtained from ntfs_mount - * @byte: Overwrite with this value - * - * Disk space is allocated in clusters. If a file isn't an exact multiple of - * the cluster size, there is some slack space at the end. Wipe this space. - * - * Return: 1 Success, the clusters were wiped - * 0 Error, something went wrong - */ -int wipe_tails (ntfs_volume *vol, int byte) -{ - if (!vol || (byte < 0)) - return 0; - - Iprintf ("wipe_tails 0x%02x\n", byte); - return 1; -} - -/** - * wipe_mft - Wipe the MFT slack space - * @vol: An ntfs volume obtained from ntfs_mount - * @byte: Overwrite with this value - * - * MFT Records are 1024 bytes long, but some of this space isn't used. Wipe any - * unused space at the end of the record and wipe any unused records. - * - * Return: 1 Success, the clusters were wiped - * 0 Error, something went wrong - */ -int wipe_mft (ntfs_volume *vol, int byte) -{ - if (!vol || (byte < 0)) - return 0; - - Iprintf ("wipe_mft 0x%02x\n", byte); - return 1; -} - -/** - * wipe_directory - Wipe the directiry indexes - * @vol: An ntfs volume obtained from ntfs_mount - * @byte: Overwrite with this value - * - * Directories are kept in sorted B+ Trees. Index blocks may not be full. Wipe - * the unused space at the ends of these blocks. - * - * Return: 1 Success, the clusters were wiped - * 0 Error, something went wrong - */ -int wipe_directory (ntfs_volume *vol, int byte) -{ - if (!vol || (byte < 0)) - return 0; - - Iprintf ("wipe_directory 0x%02x\n", byte); - return 1; -} - -/** - * wipe_logfile - Wipe the logfile (journal) - * @vol: An ntfs volume obtained from ntfs_mount - * @byte: Overwrite with this value - * - * The logfile journals the metadata to give the volume fault-tolerance. If the - * volume is in a consistant state, then this information can be erased. - * - * Return: 1 Success, the clusters were wiped - * 0 Error, something went wrong - */ -int wipe_logfile (ntfs_volume *vol, int byte) -{ - if (!vol || (byte < 0)) - return 0; - - Iprintf ("wipe_logfile 0x%02x\n", byte); - return 1; -} - -/** - * wipe_pagefile - Wipe the pagefile (swap space) - * @vol: An ntfs volume obtained from ntfs_mount - * @byte: Overwrite with this value - * - * pagefile.sys is used by Windows as extra virtual memory (swap space). - * Windows recreates the file at bootup, so it can be wiped without harm. - * - * Return: 1 Success, the clusters were wiped - * 0 Error, something went wrong - */ -int wipe_pagefile (ntfs_volume *vol, int byte) -{ - if (!vol || (byte < 0)) - return 0; - - Iprintf ("wipe_pagefile 0x%02x\n", byte); - return 1; -} - -/** - * ntfs_info - Display information about the NTFS Volume - * @vol: An ntfs volume obtained from ntfs_mount - * - * Tell the user how much could be cleaned up. List the number of free - * clusters, MFT records, etc. - * - * Return: 1 Success, displayed some info - * 0 Error, something went wrong - */ -int ntfs_info (ntfs_volume *vol) -{ - if (!vol) - return 0; - - Iprintf ("ntfs_info\n"); - return 1; -} - - -/** - * version - Print version information about the program - * - * Print a copyright statement and a brief description of the program. - * - * Return: none - */ -void version (void) -{ - Iprintf ("%s v%s Copyright (C) 2002 %s\nOverwrite the unused space on " - "an NTFS Volume\n\n%s is free software, released under the GNU " - "General Public License\nand you are welcome to redistribute " - "it under certain conditions.\n%s comes with ABSOLUTELY NO " - "WARRANTY; for details read the GNU\nGeneral Public License " - "to be found in the file COPYING in the main\nLinux-NTFS " - "distribution directory.\n\n", - EXEC_NAME, VERSION, AUTHOR, EXEC_NAME, EXEC_NAME); -} - -/** - * usage - Print a list of the parameters to the program - * - * Print a list of the parameters and options for the program. - * - * Return: none - */ -void usage (void) -{ - Iprintf ("Usage: %s [options] device\n" - " -i --info Show volume information (default)\n" - "\n" - " -d --directory Wipe directory indexes\n" - " -l --logfile Wipe the logfile (journal)\n" - " -m --mft Wipe mft space\n" - " -p --pagefile Wipe pagefile (swap space)\n" - " -t --tails Wipe file tails\n" - " -u --unused Wipe unused clusters\n" - "\n" - " -a --all Wipe all unused space\n" - "\n" - " -c num --count num Number of times to write (default = 1)\n" - " -b list --bytes list List of values to write (default = 0)\n" - "\n" - " -n --no-action Do not write to disk\n" - " -f --force Use less caution\n" - " -q --quiet Less output\n" - " -v --verbose More output\n" - " -V --version Version information\n" - " -h --help Print this help\n\n", - EXEC_NAME); - Iprintf ("Please report bugs to: linux-ntfs-dev@lists.sf.net\n\n"); -} - -/** - * parse_list - Read a comma-separated list of numbers - * @list: The comma-separated list of numbers - * @result: Store the parsed list here (must be freed by caller) - * - * Read a comma-separated list of numbers and allocate an array of ints to store - * them in. The numbers can be in decimal, octal or hex. - * - * N.B. The caller must free the memory returned in @result. - * N.B. If the function fails, @result is not changed. - * - * Return: 0 Error, invalid string - * n Success, the count of numbers parsed - */ -int parse_list (const char *list, int **result) -{ - const char *ptr; - char *end; - int i; - int count; - int *mem = NULL; - - if (!list || !result) - return 0; - - for (count = 0, ptr = list; ptr; ptr = strchr (ptr+1, ',')) - count++; - - mem = malloc ((count+1) * sizeof (int)); - if (!mem) { - Eprintf ("Couldn't allocate memory in parse_list().\n"); - return 0; - } - - memset (mem, 0xFF, (count+1) * sizeof (int)); - - for (ptr = list, i = 0; i < count; i++) { - - end = NULL; - mem[i] = strtol (ptr, &end, 0); - - if (!end || (end == ptr) || ((*end != ',') && (*end != 0))) { - Eprintf ("Invalid list '%s'\n", list); - free (mem); - return 0; - } - - if ((mem[i] < 0) || (mem[i] > 255)) { - Eprintf ("Bytes must be in range 0-255.\n"); - free (mem); - return 0; - } - - ptr = end + 1; - } - - Dprintf ("Parsing list '%s' - ", list); - for (i = 0; i <= count; i++) - Dprintf ("0x%02x ", mem[i]); - Dprintf ("\n"); - - *result = mem; - return count; -} - -/** - * parse_options - Read and validate the programs command line - * - * Read the command line, verify the syntax and parse the options. - * This function is very long, but quite simple. - * - * Return: 1 Success - * 0 Error, one or more problems - */ -int parse_options (int argc, char *argv[]) -{ - static const char *sopt = "-ab:c:dfhilmnpqtuvV"; - static const struct option lopt[] = { - { "all", no_argument, NULL, 'a' }, - { "bytes", required_argument, NULL, 'b' }, - { "count", required_argument, NULL, 'c' }, - { "directory", no_argument, NULL, 'd' }, - { "force", no_argument, NULL, 'f' }, - { "help", no_argument, NULL, 'h' }, - { "info", no_argument, NULL, 'i' }, - { "logfile", no_argument, NULL, 'l' }, - { "mft", no_argument, NULL, 'm' }, - { "no-action", no_argument, NULL, 'n' }, - { "pagefile", no_argument, NULL, 'p' }, - { "quiet", no_argument, NULL, 'q' }, - { "tails", no_argument, NULL, 't' }, - { "unused", no_argument, NULL, 'u' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - char c = -1; - char *end; - int err = 0; - int ver = 0; - int help = 0; - - opterr = 0; /* We'll handle the errors, thank you. */ - - opts.count = 1; - - while ((c = getopt_long (argc, argv, sopt, lopt, NULL)) != -1) { - switch (c) { - case 1: /* A non-option argument */ - if (!opts.device) { - opts.device = argv[optind-1]; - } else { - opts.device = NULL; - err++; - } - break; - - case 'a': - opts.directory++; - opts.logfile++; - opts.mft++; - opts.pagefile++; - opts.tails++; - opts.unused++; - break; - case 'b': - if (!opts.bytes) { - if (!parse_list (argv[optind-1], &opts.bytes)) - err++; - } else { - err++; - } - break; - case 'c': - if (opts.count == 1) { - end = NULL; - opts.count = strtol (optarg, &end, 0); - if (end && *end) - err++; - } else { - err++; - } - break; - case 'd': - opts.directory++; - break; - case 'f': - opts.force++; - break; - case 'h': - help++; - break; - case 'i': - opts.info++; - break; - case 'l': - opts.logfile++; - break; - case 'm': - opts.mft++; - break; - case 'n': - opts.noaction++; - break; - case 'p': - opts.pagefile++; - break; - case 'q': - opts.quiet++; - break; - case 't': - opts.tails++; - break; - case 'u': - opts.unused++; - break; - case 'v': - opts.verbose++; - break; - case 'V': - ver++; - break; - default: - if ((optopt == 'b') || (optopt == 'c')) { - Eprintf ("Option '%s' requires an argument.\n", argv[optind-1]); - } else { - Eprintf ("Unknown option '%s'.\n", argv[optind-1]); - } - err++; - break; - } - } - - if (help || ver) { - opts.quiet = 0; - } else { - if (opts.device == NULL) { - Eprintf ("You must specify exactly one device.\n"); - err++; - } - - if ((opts.quiet) && (opts.verbose)) { - Eprintf ("You may not use --quiet and --verbose at the same time.\n"); - err++; - } - - if (opts.info && (opts.unused || opts.tails || opts.mft || opts.directory)) { - Eprintf ("You may not use any other options with --info.\n"); - err++; - } - - if ((opts.count < 1) || (opts.count > 100)) { - Eprintf ("The iteration count must be between 1 and 100.\n"); - err++; - } - - /*if (opts.bytes && (opts.count > 0)) { - Eprintf ("You may not use both --bytes and --count.\n"); - err++; - }*/ - - /* Create a default list */ - if (!opts.bytes) { - opts.bytes = malloc (2 * sizeof (int)); - if (opts.bytes) { - opts.bytes[0] = 0; - opts.bytes[1] = -1; - } - } - - if (!opts.directory && !opts.logfile && !opts.mft && - !opts.pagefile && !opts.tails && !opts.unused) { - opts.info = 1; - } - } - - if (ver) - version(); - if (help || err) - usage(); - - return (!err && !help && !ver); -} - -/** - * valid_device - Perform some safety checks on the device, before we start - * @name: Full pathname of the device/file to work with - * @force: Continue regardless of problems - * - * Check that the name refers to a device and that is isn't already mounted. - * These checks can be overridden by using the force option. - * - * Return: 1 Success, we can continue - * 0 Error, we cannot use this device - */ -int valid_device (const char *name, int force) -{ - unsigned long mnt_flags = 0; - struct stat st; - - if (stat (name, &st) == -1) { - if (errno == ENOENT) { - Eprintf ("The device %s doesn't exist\n", name); - } else { - Eprintf ("Error getting information about %s: %s\n", name, strerror (errno)); - } - return 0; - } - - if (!S_ISBLK (st.st_mode)) { - Vprintf ("%s is not a block device.\n", name); - if (!force) { - Eprintf ("Use the force option to work with files.\n"); - return 0; - } - Vprintf ("Forced to continue.\n"); - } - - /* Make sure the file system is not mounted. */ - if (ntfs_check_if_mounted (name, &mnt_flags)) { - Vprintf ("Failed to determine whether %s is mounted: %s\n", name, strerror (errno)); - if (!force) { - Eprintf ("Use the force option to ignore this error.\n"); - return 0; - } - Vprintf ("Forced to continue.\n"); - } else if (mnt_flags & NTFS_MF_MOUNTED) { - Vprintf ("The device %s, is mounted.\n", name); - if (!force) { - Eprintf ("Use the force option to work a mounted filesystem.\n"); - return 0; - } - Vprintf ("Forced to continue.\n"); - } - - Dprintf ("Device %s, will be used\n", name); - return 1; -} - - -/** - * print_summary - Tell the use what we are about to do - * - * List the operations about to be performed. The output will be silenced by - * the --quiet option. - * - * Return: none - */ -void print_summary (void) -{ - int i; - - if (opts.noaction) - Iprintf ("%s is in 'no-action' mode, it will NOT write to disk." - "\n\n", EXEC_NAME); - - Iprintf ("%s is about to wipe:\n", EXEC_NAME); - if (opts.unused) - Iprintf ("\tunused disk space\n"); - if (opts.tails) - Iprintf ("\tfile tails\n"); - if (opts.mft) - Iprintf ("\tunused mft areas\n"); - if (opts.directory) - Iprintf ("\tunused directory index space\n"); - if (opts.logfile) - Iprintf ("\tthe logfile (journal)\n"); - if (opts.pagefile) - Iprintf ("\tthe pagefile (swap space)\n"); - - Iprintf ("\n%s will overwrite these areas with: ", EXEC_NAME); - if (opts.bytes) { - for (i = 0; opts.bytes[i] >= 0; i++) - Iprintf ("0x%02x ", opts.bytes[i]); - } - Iprintf ("\n"); - - if (opts.count > 1) - Iprintf ("%s will repeat these operations %d times.\n", EXEC_NAME, opts.count); - Iprintf ("\n"); -} - -/** - * main - Begin here - * - * Start from here. - * - * Return: 0 Success, the program worked - * 1 Error, something went wrong - */ -int main (int argc, char *argv[]) -{ - const char *locale; - ntfs_volume *vol; - int result = 1; - int flags = 0; - int i, j; - - locale = setlocale (LC_ALL, ""); - if (!locale) { - locale = setlocale (LC_ALL, NULL); - Vprintf ("Failed to set locale, using default '%s'.\n", locale); - } else { - Vprintf ("Using locale '%s'.\n", locale); - } - - if (!parse_options (argc, argv)) - return 1; - - if (!valid_device (opts.device, opts.force)) { - goto free; - } - - if (!opts.info) - print_summary(); - - if (opts.info || opts.noaction) - flags = MS_RDONLY; - vol = ntfs_mount (opts.device, flags); - if (!vol) { - Eprintf ("Couldn't mount device '%s': %s\n", opts.device, strerror (errno)); - goto free; - } - - if (vol->flags & VOLUME_IS_DIRTY) { - Iprintf ("Volume is dirty.\n"); - if (!opts.force) { - Eprintf ("Run chkdsk and try again, or use the --force option.\n"); - goto umount; - } - Iprintf ("Forced to continue.\n"); - } - - if (opts.info) { - ntfs_info (vol); - result = 0; - goto umount; - } - - /* Even if the output it quieted, you still get 5 seconds to abort. */ - if (!opts.force) { - Iprintf ("\n%s will begin in 5 seconds, press CTRL-C to abort.\n", EXEC_NAME); - sleep (5); - } - - if (!opts.bytes) { - Eprintf ("Internal error, byte list is empty\n"); - goto umount; - } - - for (i = 0; i < opts.count; i++) { - int byte; - for (j = 0; byte = opts.bytes[j], byte >= 0; j++) { - if (opts.unused && !wipe_unused (vol, byte)) - goto umount; - if (opts.tails && !wipe_tails (vol, byte)) - goto umount; - if (opts.mft && !wipe_mft (vol, byte)) - goto umount; - if (opts.directory && !wipe_directory (vol, byte)) - goto umount; - if (opts.logfile && !wipe_logfile (vol, byte)) - goto umount; - if (opts.pagefile && !wipe_pagefile (vol, byte)) - goto umount; - } - } - - result = 0; -umount: - ntfs_umount (vol, FALSE); -free: - if (opts.bytes) - free (opts.bytes); - return result; -} - - diff --git a/ntfstools/ntfswipe.h b/ntfstools/ntfswipe.h deleted file mode 100644 index f06358c2..00000000 --- a/ntfstools/ntfswipe.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * ntfswipe - Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Richard Russon - * - * This utility will overwrite usused space on an NTFS volume. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS - * distribution in the file COPYING); if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _NTFSWIPE_H_ -#define _NTFSWIPE_H_ - -#include "types.h" - -struct options { - char *device; /* Device/File to work with */ - int info; /* Show volume info */ - int force; /* Override common sense */ - int quiet; /* Less output */ - int verbose; /* Extra output */ - int noaction; /* Do not write to disk */ - int count; /* Number of iterations */ - int *bytes; /* List of overwrite characters */ - int directory; /* Wipe directory indexes */ - int logfile; /* Wipe the logfile (journal) */ - int mft; /* Wipe mft slack space */ - int pagefile; /* Wipe pagefile (swap space) */ - int tails; /* Wipe file tails */ - int unused; /* Wipe unused clusters */ -}; - -#endif /* _NTFSWIPE_H_ */ - diff --git a/ntfstools/sd.c b/ntfstools/sd.c deleted file mode 100644 index 0e0f0863..00000000 --- a/ntfstools/sd.c +++ /dev/null @@ -1,200 +0,0 @@ -#include "types.h" -#include "layout.h" - -/* - * NTFS 1.2 - System files security decriptors - * =========================================== - * - * Create the security descriptor for system file number @sys_file_no and - * return a pointer to the descriptor. - * - * $MFT, $MFTMirr, $LogFile, $AttrDef, $Bitmap, $Boot, $BadClus, and $UpCase - * are the same. - * - * $Volume, $Quota, and system files 0xb-0xf are the same. They are almost the - * same as the above, the only difference being that the two SIDs present in - * the DACL grant GENERIC_WRITE and GENERIC_READ equivalent priviledges while - * the above only grant GENERIC_READ equivalent priviledges. (For some reason - * the flags for GENERIC_READ/GENERIC_WRITE are not set by NT4, even though - * the permissions are equivalent, so we comply. - * - * Root directory system file (".") is different altogether. - * - * The sd is recturned in *@sd_val and has length *@sd_val_len. - * - * Do NOT free *@sd_val as it is static memory. This also means that you can - * only use *@sd_val until the next call to this function. - * - */ -void init_system_file_sd(int sys_file_no, char **sd_val, int *sd_val_len) -{ - static char sd_array[0x68]; - SECURITY_DESCRIPTOR_RELATIVE *sd; - ACL *acl; - ACCESS_ALLOWED_ACE *aa_ace; - SID *sid; - - if (sys_file_no < 0 || sys_file_no > 0xf) { - *sd_val = NULL; - *sd_val_len = 0; - return; - } - *sd_val = (char*)&sd_array; - sd = (SECURITY_DESCRIPTOR_RELATIVE*)&sd_array; - sd->revision = 1; - sd->alignment = 0; - sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; - if (sys_file_no == FILE_root) { - *sd_val_len = 0x50; - sd->owner = cpu_to_le32(0x30); - sd->group = cpu_to_le32(0x40); - } else { - *sd_val_len = 0x68; - sd->owner = cpu_to_le32(0x48); - sd->group = cpu_to_le32(0x58); - } - sd->sacl = cpu_to_le32(0); - sd->dacl = cpu_to_le32(0x14); - /* - * Now at offset 0x14, as specified in the security descriptor, we have - * the DACL. - */ - acl = (ACL*)((char*)sd + le32_to_cpu(sd->dacl)); - acl->revision = 2; - acl->alignment1 = 0; - if (sys_file_no == FILE_root) { - acl->size = cpu_to_le16(0x1c); - acl->ace_count = cpu_to_le16(1); - } else { - acl->size = cpu_to_le16(0x34); - acl->ace_count = cpu_to_le16(2); - } - acl->alignment2 = cpu_to_le16(0); - /* - * Now at offset 0x1c, just after the DACL's ACL, we have the first - * ACE of the DACL. The type of the ACE is access allowed. - */ - aa_ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); - aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; - if (sys_file_no == FILE_root) - aa_ace->flags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; - else - aa_ace->flags = 0; - aa_ace->size = cpu_to_le16(0x14); - switch (sys_file_no) { - case FILE_MFT: case FILE_MFTMirr: case FILE_LogFile: - case FILE_AttrDef: case FILE_Bitmap: case FILE_Boot: - case FILE_BadClus: case FILE_UpCase: - aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | - FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA; - break; - case FILE_Volume: case FILE_Secure: case 0xb ... 0xf: - aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_WRITE | - FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | - FILE_WRITE_EA | FILE_READ_EA | FILE_APPEND_DATA | - FILE_WRITE_DATA | FILE_READ_DATA; - break; - case FILE_root: - aa_ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | - FILE_READ_ATTRIBUTES | FILE_DELETE_CHILD | - FILE_TRAVERSE | FILE_WRITE_EA | FILE_READ_EA | - FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | - FILE_LIST_DIRECTORY; - break; - } - aa_ace->sid.revision = 1; - aa_ace->sid.sub_authority_count = 1; - aa_ace->sid.identifier_authority.value[0] = 0; - aa_ace->sid.identifier_authority.value[1] = 0; - aa_ace->sid.identifier_authority.value[2] = 0; - aa_ace->sid.identifier_authority.value[3] = 0; - aa_ace->sid.identifier_authority.value[4] = 0; - if (sys_file_no == FILE_root) { - /* SECURITY_WORLD_SID_AUTHORITY (S-1-1) */ - aa_ace->sid.identifier_authority.value[5] = 1; - aa_ace->sid.sub_authority[0] = - cpu_to_le32(SECURITY_WORLD_RID); - /* This is S-1-1-0, the WORLD_SID. */ - } else { - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - aa_ace->sid.identifier_authority.value[5] = 5; - aa_ace->sid.sub_authority[0] = - cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); - } - /* - * Now at offset 0x30 within security descriptor, just after the first - * ACE of the DACL. All system files, except the root directory, have - * a second ACE. - */ - if (sys_file_no != FILE_root) { - /* The second ACE of the DACL. Type is access allowed. */ - aa_ace = (ACCESS_ALLOWED_ACE*)((char*)aa_ace + - le16_to_cpu(aa_ace->size)); - aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; - aa_ace->flags = 0; - aa_ace->size = cpu_to_le16(0x18); - switch (sys_file_no) { - case FILE_MFT: case FILE_MFTMirr: - case FILE_LogFile: case FILE_AttrDef: - case FILE_Bitmap: case FILE_Boot: - case FILE_BadClus: case FILE_UpCase: - aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | - FILE_READ_ATTRIBUTES | FILE_READ_EA | - FILE_READ_DATA; - break; - case FILE_Volume: case FILE_Secure: - case 0xb ... 0xf: - aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | - FILE_WRITE_ATTRIBUTES | - FILE_READ_ATTRIBUTES | FILE_WRITE_EA | - FILE_READ_EA | FILE_APPEND_DATA | - FILE_WRITE_DATA | FILE_READ_DATA; - break; - } - aa_ace->sid.revision = 1; - aa_ace->sid.sub_authority_count = 2; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - aa_ace->sid.identifier_authority.value[0] = 0; - aa_ace->sid.identifier_authority.value[1] = 0; - aa_ace->sid.identifier_authority.value[2] = 0; - aa_ace->sid.identifier_authority.value[3] = 0; - aa_ace->sid.identifier_authority.value[4] = 0; - aa_ace->sid.identifier_authority.value[5] = 5; - aa_ace->sid.sub_authority[0] = - cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - aa_ace->sid.sub_authority[1] = - cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - /* Now at offset 0x48 into the security descriptor. */ - } - /* As specified in the security descriptor, we now have the owner SID.*/ - sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); - sid->revision = 1; - sid->sub_authority_count = 2; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - /* - * Now at offset 0x40 or 0x58 (root directory and the other system - * files, respectively) into the security descriptor, as specified in - * the security descriptor, we have the group SID. - */ - sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); - sid->revision = 1; - sid->sub_authority_count = 2; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); -} - diff --git a/ntfstools/upcase.c b/ntfstools/upcase.c deleted file mode 100644 index a184379a..00000000 --- a/ntfstools/upcase.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * $Id$ - * - * Copyright (c) 2001 Richard Russon - * Copyright (c) 2001-2002 Anton Altaparmakov - * - * Modified for mkntfs inclusion 9 June 2001 by Anton Altaparmakov. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS source - * in the file COPYING); if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#include "types.h" - -void init_upcase_table(uchar_t *uc, u32 uc_len) -{ - static int uc_run_table[][3] = { /* Start, End, Add */ - {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, - {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, - {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, - {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, - {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, - {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, - {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, - {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, - {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, - {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, - {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, - {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, - {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, - {0} - }; - static int uc_dup_table[][2] = { /* Start, End */ - {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, - {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, - {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, - {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, - {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, - {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, - {0} - }; - static int uc_byte_table[][2] = { /* Offset, Value */ - {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, - {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, - {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, - {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, - {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, - {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, - {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, - {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, - {0} - }; - int i, r; - - memset((char*)uc, 0, uc_len); - uc_len >>= 1; - for (i = 0; i < uc_len; i++) - uc[i] = i; - for (r = 0; uc_run_table[r][0]; r++) - for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) - uc[i] += uc_run_table[r][2]; - for (r = 0; uc_dup_table[r][0]; r++) - for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) - uc[i + 1]--; - for (r = 0; uc_byte_table[r][0]; r++) - uc[uc_byte_table[r][0]] = uc_byte_table[r][1]; -} -