Commit ec44dec7 authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: 2.1.8 release - If the $LogFile indicates a clean shutdown and a

      read-write (re)mount is requested, empty $LogFile by overwriting it
      with 0xff bytes to ensure that Windows cannot cause data corruption
      by replaying a stale journal after Linux has written to the volume.
parent 1660b1af
...@@ -273,6 +273,18 @@ ChangeLog ...@@ -273,6 +273,18 @@ ChangeLog
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
2.1.8:
- Read the $MFT mirror and compare it to the $MFT and if the two do not
match, force a read-only mount and do not allow read-write remounts.
- Read and parse the $LogFile journal and if it indicates that the
volume was not shutdown cleanly, force a read-only mount and do not
allow read-write remounts. If the $LogFile indicates a clean
shutdown and a read-write (re)mount is requested, empty $LogFile to
ensure that Windows cannot cause data corruption by replaying a stale
journal after Linux has written to the volume.
- Improve time handling so that the NTFS time is fully preserved when
converted to kernel time and only up to 99 nano-seconds are lost when
kernel time is converted to NTFS time.
2.1.7: 2.1.7:
- Enable NFS exporting of mounted NTFS volumes. - Enable NFS exporting of mounted NTFS volumes.
2.1.6: 2.1.6:
......
...@@ -19,7 +19,7 @@ ToDo: ...@@ -19,7 +19,7 @@ ToDo:
sufficient for synchronisation here. We then just need to make sure sufficient for synchronisation here. We then just need to make sure
ntfs_readpage/writepage/truncate interoperate properly with us. ntfs_readpage/writepage/truncate interoperate properly with us.
2.1.8-WIP 2.1.8 - Handle $MFT mirror and $LogFile, improve time ihandling, and cleanups.
- Use get_bh() instead of manual atomic_inc() in fs/ntfs/compress.c. - Use get_bh() instead of manual atomic_inc() in fs/ntfs/compress.c.
- Modify fs/ntfs/time.c::ntfs2utc(), get_current_ntfs_time(), and - Modify fs/ntfs/time.c::ntfs2utc(), get_current_ntfs_time(), and
...@@ -52,6 +52,10 @@ ToDo: ...@@ -52,6 +52,10 @@ ToDo:
think that a volume is dirty when in fact it is clean. This should think that a volume is dirty when in fact it is clean. This should
only affect volumes that have not been shutdown cleanly and did not only affect volumes that have not been shutdown cleanly and did not
have any pending, non-check-pointed i/o. have any pending, non-check-pointed i/o.
- If the $LogFile indicates a clean shutdown and a read-write (re)mount
is requested, empty $LogFile by overwriting it with 0xff bytes to
ensure that Windows cannot cause data corruption by replaying a stale
journal after Linux has written to the volume.
2.1.7 - Enable NFS exporting of mounted NTFS volumes. 2.1.7 - Enable NFS exporting of mounted NTFS volumes.
......
...@@ -5,7 +5,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o ...@@ -5,7 +5,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o
ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o logfile.o \ ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o logfile.o \
mft.o mst.o namei.o super.o sysctl.o unistr.o upcase.o mft.o mst.o namei.o super.o sysctl.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.8-WIP\" EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.8\"
ifeq ($(CONFIG_NTFS_DEBUG),y) ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
......
...@@ -662,4 +662,76 @@ BOOL ntfs_is_logfile_clean(struct inode *log_vi) ...@@ -662,4 +662,76 @@ BOOL ntfs_is_logfile_clean(struct inode *log_vi)
return FALSE; return FALSE;
} }
/**
* ntfs_empty_logfile - empty the contents of the $LogFile journal
* @log_vi: struct inode of loaded journal $LogFile to empty
*
* Empty the contents of the $LogFile journal @log_vi and return TRUE on
* success FALSE on error.
*
* This function assumes that the $LogFile journal has already been consistency
* checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
* has been used to ensure that the $LogFile is clean.
*/
BOOL ntfs_empty_logfile(struct inode *log_vi)
{
ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
struct address_space *mapping;
pgoff_t idx, end;
ntfs_debug("Entering.");
if (NVolLogFileEmpty(vol))
goto done;
mapping = log_vi->i_mapping;
end = (log_vi->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
for (idx = 0; idx < end; ++idx) {
struct page *page;
u8 *kaddr;
/* Find or create the current page. (The page is locked.) */
page = grab_cache_page(mapping, idx);
if (unlikely(!page)) {
ntfs_error(vol->sb, "Insufficient memory to grab "
"$LogFile page (index %lu).", idx);
return FALSE;
}
/*
* Set all bytes in the page to 0xff. It doesn't matter if we
* go beyond i_size, because ntfs_writepage() will take care of
* that for us.
*/
kaddr = (u8*)kmap_atomic(page, KM_USER0);
memset(kaddr, 0xff, PAGE_CACHE_SIZE);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
/*
* If the page has buffers, mark them uptodate since buffer
* state and not page state is definitive in 2.6 kernels.
*/
if (page_has_buffers(page)) {
struct buffer_head *bh, *head;
bh = head = page_buffers(page);
do {
set_buffer_uptodate(bh);
} while ((bh = bh->b_this_page) != head);
}
/* Now that buffers are uptodate, set the page uptodate, too. */
SetPageUptodate(page);
/*
* Set the page and all its buffers dirty and mark the inode
* dirty, too. The VM will write the page later on.
*/
set_page_dirty(page);
/* Finally unlock and release the page. */
unlock_page(page);
page_cache_release(page);
}
/* We set the flag so we do not clear the log file again on remount. */
NVolSetLogFileEmpty(vol);
done:
ntfs_debug("Done.");
return TRUE;
}
#endif /* NTFS_RW */ #endif /* NTFS_RW */
...@@ -298,6 +298,8 @@ extern BOOL ntfs_check_logfile(struct inode *log_vi); ...@@ -298,6 +298,8 @@ extern BOOL ntfs_check_logfile(struct inode *log_vi);
extern BOOL ntfs_is_logfile_clean(struct inode *log_vi); extern BOOL ntfs_is_logfile_clean(struct inode *log_vi);
extern BOOL ntfs_empty_logfile(struct inode *log_vi);
#endif /* NTFS_RW */ #endif /* NTFS_RW */
#endif /* _LINUX_NTFS_LOGFILE_H */ #endif /* _LINUX_NTFS_LOGFILE_H */
...@@ -299,7 +299,7 @@ static BOOL parse_options(ntfs_volume *vol, char *opt) ...@@ -299,7 +299,7 @@ static BOOL parse_options(ntfs_volume *vol, char *opt)
* *
* Change the mount options of an already mounted ntfs filesystem. * Change the mount options of an already mounted ntfs filesystem.
* *
* NOTE: The VFS set the @sb->s_flags remount flags to @flags after * NOTE: The VFS sets the @sb->s_flags remount flags to @flags after
* ntfs_remount() returns successfully (i.e. returns 0). Otherwise, * ntfs_remount() returns successfully (i.e. returns 0). Otherwise,
* @sb->s_flags are not changed. * @sb->s_flags are not changed.
*/ */
...@@ -308,26 +308,33 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) ...@@ -308,26 +308,33 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
ntfs_volume *vol = NTFS_SB(sb); ntfs_volume *vol = NTFS_SB(sb);
ntfs_debug("Entering with remount options string: %s", opt); ntfs_debug("Entering with remount options string: %s", opt);
#ifndef NTFS_RW #ifndef NTFS_RW
/* For read-only compiled driver, enforce all read-only flags. */ /* For read-only compiled driver, enforce all read-only flags. */
*flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; *flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
#else #else /* ! NTFS_RW */
/* /*
* For the read-write compiled driver, if we are remounting read-write, * For the read-write compiled driver, if we are remounting read-write,
* make sure there aren't any volume errors. * make sure there aren't any volume errors and empty the lofgile.
*/ */
if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) { if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) {
static const char *es = ". Cannot remount read-write.";
if (NVolErrors(vol)) { if (NVolErrors(vol)) {
ntfs_error(sb, "Volume has errors and is read-only. " ntfs_error(sb, "Volume has errors and is read-only%s",
"Cannot remount read-write."); es);
return -EROFS;
}
if (!ntfs_empty_logfile(vol->logfile_ino)) {
ntfs_error(sb, "Failed to empty journal $LogFile%s",
es);
NVolSetErrors(vol);
return -EROFS; return -EROFS;
} }
} }
// TODO: For now we enforce no atime and dir atime updates as they are // TODO: For now we enforce no atime and dir atime updates as they are
// not implemented. // not implemented.
*flags |= MS_NOATIME | MS_NODIRATIME; *flags |= MS_NOATIME | MS_NODIRATIME;
#endif #endif /* ! NTFS_RW */
// FIXME/TODO: If left like this we will have problems with rw->ro and // FIXME/TODO: If left like this we will have problems with rw->ro and
// ro->rw, as well as with sync->async and vice versa remounts. // ro->rw, as well as with sync->async and vice versa remounts.
...@@ -345,7 +352,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) ...@@ -345,7 +352,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
if (!parse_options(vol, opt)) if (!parse_options(vol, opt))
return -EINVAL; return -EINVAL;
ntfs_debug("Done.");
return 0; return 0;
} }
...@@ -1156,10 +1163,25 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1156,10 +1163,25 @@ static BOOL load_system_files(ntfs_volume *vol)
!vol->logfile_ino ? es1 : es2, es3); !vol->logfile_ino ? es1 : es2, es3);
/* This will prevent a read-write remount. */ /* This will prevent a read-write remount. */
NVolSetErrors(vol); NVolSetErrors(vol);
/* If a read-write mount, empty the logfile. */
} else if (!(sb->s_flags & MS_RDONLY) &&
!ntfs_empty_logfile(vol->logfile_ino)) {
static const char *es1 = "Failed to empty $LogFile";
static const char *es2 = ". Mount in Windows.";
/* Convert to a read-only mount. */
if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE))) {
ntfs_error(sb, "%s and neither on_errors=continue nor "
"on_errors=remount-ro was specified%s",
es1, es2);
goto iput_logfile_err_out;
}
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
/* This will prevent a read-write remount. */
NVolSetErrors(vol);
} }
// FIXME: Empty the logfile, but only if not read-only.
// FIXME: What happens if someone remounts rw? We need to empty the file
// then. We need a flag to tell us whether we have done it already.
#endif #endif
/* /*
* Get the inode for the attribute definitions file and parse the * Get the inode for the attribute definitions file and parse the
...@@ -2124,4 +2146,3 @@ MODULE_PARM_DESC(debug_msgs, "Enable debug messages."); ...@@ -2124,4 +2146,3 @@ MODULE_PARM_DESC(debug_msgs, "Enable debug messages.");
module_init(init_ntfs_fs) module_init(init_ntfs_fs)
module_exit(exit_ntfs_fs) module_exit(exit_ntfs_fs)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment