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);