From 5302d23f7bf303be59a554fd63d4b2581199a8c5 Mon Sep 17 00:00:00 2001 From: aia21 Date: Tue, 28 Nov 2006 10:09:56 +0000 Subject: [PATCH] - Empty the journal at mount time. (Anton) - Set the volume dirty bit at mount time (if it is not set already and clear it again at umount time but only if it was not set to start with. (Anton) --- ChangeLog | 4 ++ include/ntfs/volume.h | 16 ++++++++ libntfs/volume.c | 35 ++++++++++++++++-- ntfsprogs/ntfscat.c | 14 +++++-- ntfsprogs/ntfscat.h | 1 + ntfsprogs/ntfsclone.c | 14 ++++--- ntfsprogs/ntfscp.c | 2 +- ntfsprogs/ntfsdump_logfile.c | 2 +- ntfsprogs/ntfsfix.c | 72 +++++++----------------------------- ntfsprogs/ntfsinfo.c | 2 +- ntfsprogs/ntfsmount.c | 16 -------- ntfsprogs/ntfsmove.c | 5 +-- ntfsprogs/ntfsresize.c | 29 +++------------ ntfsprogs/ntfswipe.c | 2 +- ntfsprogs/utils.c | 2 +- 15 files changed, 96 insertions(+), 120 deletions(-) diff --git a/ChangeLog b/ChangeLog index 140e561e..c08c0adc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -80,6 +80,10 @@ xx/xx/2006 - x.xx.x - . - Introduce MNT_NTFS_NOT_EXCLUSIVE mount option that tells libntfs do not open volume exclusively. Useful if libntfs user cares about this himself, eg. FUSE with blkdev option. (Yura) + - Empty the journal at mount time. (Anton) + - Set the volume dirty bit at mount time (if it is not set already and + clear it again at umount time but only if it was not set to start + with. (Anton) 21/06/2006 - 1.13.1 - Various fixes. diff --git a/include/ntfs/volume.h b/include/ntfs/volume.h index 96ace852..ef79e3b9 100644 --- a/include/ntfs/volume.h +++ b/include/ntfs/volume.h @@ -60,6 +60,10 @@ typedef enum { NTFS_MNT_NOATIME = 2, NTFS_MNT_CASE_SENSITIVE = 4, NTFS_MNT_NOT_EXCLUSIVE = 8, + NTFS_MNT_FORENSIC = 16, /* Mount for forensic purposes, i.e. do + not do any writing at all during the + mount, i.e. no journal emptying, no + dirty bit setting, etc. */ } ntfs_mount_flags; /** @@ -85,6 +89,10 @@ typedef enum { NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ NV_LogFileEmpty, /* 1: $logFile journal is empty. */ NV_NoATime, /* 1: Do not update access time. */ + NV_WasDirty, /* 1: Volume was marked dirty before we mounted + it. */ + NV_ForensicMount, /* 1: Mount is forensic, i.e. no modifications + are to be done by mount/umount. */ } ntfs_volume_state_bits; #define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) @@ -107,6 +115,14 @@ typedef enum { #define NVolSetNoATime(nv) set_nvol_flag(nv, NoATime) #define NVolClearNoATime(nv) clear_nvol_flag(nv, NoATime) +#define NVolWasDirty(nv) test_nvol_flag(nv, WasDirty) +#define NVolSetWasDirty(nv) set_nvol_flag(nv, WasDirty) +#define NVolClearWasDirty(nv) clear_nvol_flag(nv, WasDirty) + +#define NVolForensicMount(nv) test_nvol_flag(nv, ForensicMount) +#define NVolSetForensicMount(nv) set_nvol_flag(nv, ForensicMount) +#define NVolClearForensicMount(nv) clear_nvol_flag(nv, ForensicMount) + /* * NTFS version 1.1 and 1.2 are used by Windows NT4. * NTFS version 2.x is used by Windows 2000 Beta diff --git a/libntfs/volume.c b/libntfs/volume.c index e3d08c17..77cc3c75 100644 --- a/libntfs/volume.c +++ b/libntfs/volume.c @@ -89,6 +89,14 @@ ntfs_volume *ntfs_volume_alloc(void) */ static void __ntfs_volume_release(ntfs_volume *v) { + /* + * Clear the dirty bit if it was not set before we mounted and this is + * not a forensic mount. + */ + if (!NVolReadOnly(v) && !NVolWasDirty(v) && !NVolForensicMount(v)) { + v->flags &= ~VOLUME_IS_DIRTY; + (void)ntfs_volume_write_flags(v, v->flags); + } if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni)) ntfs_inode_sync(v->lcnbmp_ni); if (v->vol_ni) @@ -788,7 +796,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) ntfs_log_perror("Failed to startup volume"); return NULL; } - + /* Record whether this is a forensic mount. */ + if (flags & NTFS_MNT_FORENSIC) + NVolSetForensicMount(vol); /* Load data from $MFT and $MFTMirr and compare the contents. */ m = (u8*)ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); m2 = (u8*)ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); @@ -1002,9 +1012,14 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) /* Setup vol from the volume information attribute value. */ vol->major_ver = vinf->major_ver; vol->minor_ver = vinf->minor_ver; - /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are - defined using cpu_to_le16() macro and hence are consistent. */ + /* + * Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are defined + * using cpu_to_le16() macro and hence are consistent. + */ vol->flags = vinf->flags; + /* Record whether the volume was dirty or not. */ + if (vol->flags & VOLUME_IS_DIRTY) + NVolSetWasDirty(vol); /* * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. */ @@ -1116,12 +1131,26 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) /* * Check for dirty logfile and hibernated Windows. * We care only about read-write mounts. + * + * If all is ok, reset the logfile and set the dirty bit on the volume. + * + * But do not do that if this is a FORENSIC mount. */ if (!(flags & NTFS_MNT_RDONLY)) { if (ntfs_volume_check_logfile(vol) < 0) goto error_exit; if (ntfs_volume_check_hiberfile(vol) < 0) goto error_exit; + if (!NVolForensicMount(vol)) { + if (ntfs_logfile_reset(vol) < 0) + goto error_exit; + if (!NVolWasDirty(vol)) { + vol->flags |= VOLUME_IS_DIRTY; + if (ntfs_volume_write_flags(vol, vol->flags) < + 0) + goto error_exit; + } + } } return vol; diff --git a/ntfsprogs/ntfscat.c b/ntfsprogs/ntfscat.c index d584bd4f..57d2af69 100644 --- a/ntfsprogs/ntfscat.c +++ b/ntfsprogs/ntfscat.c @@ -86,7 +86,8 @@ static void usage(void) " -q, --quiet Less output\n" " -V, --version Version information\n" " -v, --verbose More output\n\n", - //" -r --raw Display the compressed or encrypted file", +// Does not work for compressed files at present so leave undocumented... +// " -r --raw Display the raw data (e.g. for compressed or encrypted file)", EXEC_NAME); ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); } @@ -156,7 +157,7 @@ static int parse_attribute(const char *value, ATTR_TYPES *attr) */ static int parse_options(int argc, char **argv) { - static const char *sopt = "-a:fh?i:n:qVv"; + static const char *sopt = "-a:fh?i:n:qVvr"; static const struct option lopt[] = { { "attribute", required_argument, NULL, 'a' }, { "attribute-name", required_argument, NULL, 'n' }, @@ -166,6 +167,7 @@ static int parse_options(int argc, char **argv) { "quiet", no_argument, NULL, 'q' }, { "version", no_argument, NULL, 'V' }, { "verbose", no_argument, NULL, 'v' }, + { "raw", no_argument, NULL, 'r' }, { NULL, 0, NULL, 0 } }; @@ -247,6 +249,9 @@ static int parse_options(int argc, char **argv) opts.verbose++; ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); break; + case 'r': + opts.raw = TRUE; + break; default: ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); err++; @@ -349,10 +354,11 @@ static int cat(ntfs_volume *vol, ntfs_inode *inode, ATTR_TYPES type, offset = 0; for (;;) { - if (block_size > 0) { + if (!opts.raw && block_size > 0) { // These types have fixup bytes_read = ntfs_attr_mst_pread(attr, offset, 1, block_size, buffer); - bytes_read *= block_size; + if (bytes_read > 0) + bytes_read *= block_size; } else { bytes_read = ntfs_attr_pread(attr, offset, bufsize, buffer); } diff --git a/ntfsprogs/ntfscat.h b/ntfsprogs/ntfscat.h index 75b2b068..cf474b48 100644 --- a/ntfsprogs/ntfscat.h +++ b/ntfsprogs/ntfscat.h @@ -38,6 +38,7 @@ struct options { int force; /* Override common sense */ int quiet; /* Less output */ int verbose; /* Extra output */ + BOOL raw; /* Raw data output */ }; #endif /* _NTFSCAT_H_ */ diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index 474fa881..54d3a5e0 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -1388,7 +1388,7 @@ static void mount_volume(unsigned long new_mntflag) exit(1); } - if (vol->flags & VOLUME_IS_DIRTY) + if (NVolWasDirty(vol)) if (opt.force-- <= 0) err_exit(dirty_volume_msg, opt.volume); @@ -1538,17 +1538,18 @@ static s64 open_image(void) if (memcmp(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) != 0) err_exit("Input file is not an image! (invalid magic)\n"); if (image_hdr.major_ver < NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE) { - Printf("Old image format detected. Byteswapping on big " - "endian architectures. If the image was " - "created on a little endian architecture it " - "will not work. Use a more recent version " - "of ntfsclone to recreate the image.\n"); image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR; image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR; +#if (__BYTE_ORDER == __BIG_ENDIAN) + Printf("Old image format detected. If the image was created " + "on a little endian architecture it will not " + "work. Use a more recent version of " + "ntfsclone to recreate the image.\n"); image_hdr.cluster_size = cpu_to_le32(image_hdr.cluster_size); image_hdr.device_size = cpu_to_sle64(image_hdr.device_size); image_hdr.nr_clusters = cpu_to_sle64(image_hdr.nr_clusters); image_hdr.inuse = cpu_to_sle64(image_hdr.inuse); +#endif image_hdr.offset_to_image_data = const_cpu_to_le32((sizeof(image_hdr) + 7) & ~7); image_is_host_endian = TRUE; @@ -1764,6 +1765,7 @@ int main(int argc, char **argv) device_size = open_volume(); ntfs_size = vol->nr_clusters * vol->cluster_size; } + // FIXME: This needs to be the cluster size... ntfs_size += 512; /* add backup boot sector */ if (opt.std_out) { diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c index 74d327cd..c53ca0fe 100644 --- a/ntfsprogs/ntfscp.c +++ b/ntfsprogs/ntfscp.c @@ -352,7 +352,7 @@ int main(int argc, char *argv[]) return 1; } - if ((vol->flags & VOLUME_IS_DIRTY) && (!opts.force)) + if (NVolWasDirty(vol) && !opts.force) goto umount; { diff --git a/ntfsprogs/ntfsdump_logfile.c b/ntfsprogs/ntfsdump_logfile.c index 2597dcfd..f636ca93 100644 --- a/ntfsprogs/ntfsdump_logfile.c +++ b/ntfsprogs/ntfsdump_logfile.c @@ -197,7 +197,7 @@ static int logfile_open(BOOL is_volume, const char *filename, ntfs_inode *ni; ntfs_attr *na; - vol = ntfs_mount(filename, NTFS_MNT_RDONLY); + vol = ntfs_mount(filename, NTFS_MNT_RDONLY | NTFS_MNT_FORENSIC); if (!vol) log_err_exit(NULL, "Failed to mount %s: %s\n", filename, strerror(errno)); diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index 71a3d3a0..28354ade 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -82,8 +82,6 @@ switch if you want to be able to build the NTFS utilities." static const char *EXEC_NAME = "ntfsfix"; static const char *OK = "OK\n"; static const char *FAILED = "FAILED\n"; -static BOOL vol_is_dirty = FALSE; -static BOOL journal_is_empty = FALSE; struct { char *volume; @@ -247,9 +245,8 @@ static int set_dirty_flag(ntfs_volume *vol) { u16 flags; - if (vol_is_dirty == TRUE) + if (NVolWasDirty(vol)) return 0; - ntfs_log_info("Setting required flags on partition... "); /* * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run @@ -264,37 +261,9 @@ static int set_dirty_flag(ntfs_volume *vol) ntfs_log_error("Error setting volume flags.\n"); return -1; } + vol->flags = flags; ntfs_log_info(OK); - vol_is_dirty = TRUE; - return 0; -} - -/** - * set_dirty_flag_mount - */ -static int set_dirty_flag_mount(ntfs_volume *vol) -{ - u16 flags; - - if (vol_is_dirty == TRUE) - return 0; - - ntfs_log_info("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_volume_write_flags(vol, flags)) { - ntfs_log_info(FAILED); - ntfs_log_error("Error setting volume flags.\n"); - return -1; - } - ntfs_log_info(OK); - vol_is_dirty = TRUE; + NVolSetWasDirty(vol); return 0; } @@ -303,9 +272,8 @@ static int set_dirty_flag_mount(ntfs_volume *vol) */ static int empty_journal(ntfs_volume *vol) { - if (journal_is_empty == TRUE) + if (NVolLogFileEmpty(vol)) return 0; - ntfs_log_info("Going to empty the journal ($LogFile)... "); if (ntfs_logfile_reset(vol)) { ntfs_log_info(FAILED); @@ -313,7 +281,6 @@ static int empty_journal(ntfs_volume *vol) return -1; } ntfs_log_info(OK); - journal_is_empty = TRUE; return 0; } @@ -475,13 +442,13 @@ static int fix_mount(void) ntfs_log_info("Attempting to correct errors... "); - dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, NULL); + dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, + NULL); if (!dev) { ntfs_log_info(FAILED); ntfs_log_perror("Failed to allocate device"); return -1; } - vol = ntfs_volume_startup(dev, 0); if (!vol) { ntfs_log_info(FAILED); @@ -490,17 +457,12 @@ static int fix_mount(void) ntfs_device_free(dev); return -1; } - if (fix_mftmirr(vol) < 0) goto error_exit; - - /* FIXME: Will this fail? Probably... */ if (set_dirty_flag(vol) < 0) goto error_exit; - if (empty_journal(vol) < 0) goto error_exit; - ret = 0; error_exit: /* ntfs_umount() will invoke ntfs_device_free() for us. */ @@ -550,7 +512,8 @@ int main(int argc, char **argv) exit(1); } } - + /* So the unmount does not clear it again. */ + NVolSetWasDirty(vol); /* Check NTFS version is ok for us (in $Volume) */ ntfs_log_info("NTFS volume version is %i.%i.\n", vol->major_ver, vol->minor_ver); @@ -558,22 +521,13 @@ int main(int argc, char **argv) ntfs_log_error("Error: Unknown NTFS version.\n"); goto error_exit; } - - if (set_dirty_flag_mount(vol) < 0) - goto error_exit; - - if (empty_journal(vol) < 0) - goto error_exit; - 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: If on NTFS 3.0+, check for presence of the usn + * journal and stamp it if present. + */ } - - /* FIXME: Should we be marking the quota out of date, too? */ - + /* FIXME: We should be marking the quota out of date, too. */ /* That's all for now! */ ntfs_log_info("NTFS partition %s was processed successfully.\n", vol->dev->d_name); diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 91e34f0a..2fbd77c5 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -1288,7 +1288,7 @@ static void ntfs_dump_attribute_header(ATTR_RECORD *a, ntfs_volume *vol) // TODO: Switch this to properly aligned hex... printf("\tRunlist:\tVCN\t\tLCN\t\tLength\n"); while (rlc->length) { - printf("\t\t\t%lld\t\t%lld\t\t%lld\n", + printf("\t\t\t0x%llx\t\t0x%llx\t\t0x%llx\n", rlc->vcn, rlc->lcn, rlc->length); rlc++; } diff --git a/ntfsprogs/ntfsmount.c b/ntfsprogs/ntfsmount.c index 67f89ccc..16f089f8 100644 --- a/ntfsprogs/ntfsmount.c +++ b/ntfsprogs/ntfsmount.c @@ -101,7 +101,6 @@ typedef struct { BOOL debug; BOOL noatime; BOOL no_detach; - BOOL leave_dirty; } ntfs_fuse_context_t; typedef enum { @@ -1360,10 +1359,6 @@ exit: static void ntfs_fuse_destroy(void *priv __attribute__((unused))) { if (ctx->vol) { - if (!ctx->leave_dirty && ntfs_volume_write_flags(ctx->vol, - ctx->vol->flags & ~VOLUME_IS_DIRTY)) - ntfs_log_error("Failed to clear volume dirty flag. " - "OK, leave it, chkdsk will handle.\n"); ntfs_log_info("Unmounting %s (%s)\n", opts.device, ctx->vol->vol_name); if (ntfs_umount(ctx->vol, FALSE)) @@ -1431,17 +1426,6 @@ static int ntfs_fuse_mount(const char *device) return -1; } ctx->vol = vol; - if (vol->flags & VOLUME_IS_DIRTY) - ctx->leave_dirty = TRUE; - else { - if (ntfs_volume_write_flags(vol, vol->flags | - VOLUME_IS_DIRTY)) { - ntfs_log_perror("Failed to set temporary dirty flag"); - ntfs_umount(vol, FALSE); - ctx->vol = NULL; - return -1; - } - } return 0; } diff --git a/ntfsprogs/ntfsmove.c b/ntfsprogs/ntfsmove.c index c7e1bc2b..bda2b88c 100644 --- a/ntfsprogs/ntfsmove.c +++ b/ntfsprogs/ntfsmove.c @@ -891,10 +891,7 @@ int main(int argc, char *argv[]) count = move_file(vol, inode, opts.location, 0); if ((count > 0) && (!opts.nodirty)) { - if (ntfs_volume_write_flags(vol, vol->flags | VOLUME_IS_DIRTY) < - 0) { - ntfs_log_error("Couldn't mark volume dirty\n"); - } + NVolSetWasDirty(vol); ntfs_log_info("Relocated %lld bytes\n", count); } if (count >= 0) diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index 47d59aae..719febfe 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -2255,7 +2255,7 @@ static ntfs_volume *mount_volume(void) exit(1); } - if (vol->flags & VOLUME_IS_DIRTY) + if (NVolWasDirty(vol)) if (opt.force-- <= 0) err_exit("Volume is scheduled for check.\nRun chkdsk /f" " and please try again, or see option -f.\n"); @@ -2279,32 +2279,16 @@ static ntfs_volume *mount_volume(void) /** * prepare_volume_fixup * - * Set the volume's dirty flag and wipe the filesystem journal. When Windows - * boots it will automatically run chkdsk to check for any problems. If the - * read-only command line option was given, this function will do nothing. + * Make sure the volume's dirty flag does not get cleared at umount time. When + * Windows boots it will automatically run chkdsk to check for any problems. + * If the read-only command line option was given, this function will do + * nothing. */ static void prepare_volume_fixup(ntfs_volume *vol) { - u16 flags; - - flags = vol->flags | VOLUME_IS_DIRTY; - if (vol->major_ver >= 2) - flags |= VOLUME_MOUNTED_ON_NT4; - printf("Schedule chkdsk for NTFS consistency check at Windows " "boot time ...\n"); - - if (ntfs_volume_write_flags(vol, flags)) - perr_exit("Failed to set $Volume dirty"); - - if (vol->dev->d_ops->sync(vol->dev) == -1) - perr_exit("Failed to sync device"); - - printf("Resetting $LogFile ... (this might take a while)\n"); - - if (ntfs_logfile_reset(vol)) - perr_exit("Failed to reset $LogFile"); - + NVolSetWasDirty(vol); if (vol->dev->d_ops->sync(vol->dev) == -1) perr_exit("Failed to sync device"); } @@ -2470,7 +2454,6 @@ int main(int argc, char **argv) proceed_question(); } - /* FIXME: performance - relocate logfile here if it's needed */ prepare_volume_fixup(vol); if (resize.relocations) diff --git a/ntfsprogs/ntfswipe.c b/ntfsprogs/ntfswipe.c index 3f66f92d..78acc94b 100644 --- a/ntfsprogs/ntfswipe.c +++ b/ntfsprogs/ntfswipe.c @@ -1346,7 +1346,7 @@ int main(int argc, char *argv[]) if (!vol) goto free; - if ((vol->flags & VOLUME_IS_DIRTY) && (!opts.force)) + if (NVolWasDirty(vol) && !opts.force) goto umount; if (opts.info) { diff --git a/ntfsprogs/utils.c b/ntfsprogs/utils.c index 807d789e..f2bc43ad 100644 --- a/ntfsprogs/utils.c +++ b/ntfsprogs/utils.c @@ -229,7 +229,7 @@ ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, return NULL; } - if (vol->flags & VOLUME_IS_DIRTY) { + if (NVolWasDirty(vol)) { if (!force) { ntfs_log_error("%s", dirty_volume_msg); ntfs_umount(vol, FALSE);