return FALSE;
}
+#ifdef NTFS_RW
+
+/**
+ * ntfs_write_volume_flags - write new flags to the volume information flags
+ * @vol: ntfs volume on which to modify the flags
+ * @flags: new flags value for the volume information flags
+ *
+ * Internal function. You probably want to use ntfs_{set,clear}_volume_flags()
+ * instead (see below).
+ *
+ * Replace the volume information flags on the volume @vol with the value
+ * supplied in @flags. Note, this overwrites the volume information flags, so
+ * make sure to combine the flags you want to modify with the old flags and use
+ * the result when calling ntfs_write_volume_flags().
+ *
+ * Return 0 on success and -errno on error.
+ */
+static int ntfs_write_volume_flags(ntfs_volume *vol, const VOLUME_FLAGS flags)
+{
+ ntfs_inode *ni = NTFS_I(vol->vol_ino);
+ MFT_RECORD *m;
+ VOLUME_INFORMATION *vi;
+ attr_search_context *ctx;
+ int err;
+
+ ntfs_debug("Entering, old flags = 0x%x, new flags = 0x%x.",
+ vol->vol_flags, flags);
+ if (vol->vol_flags == flags)
+ goto done;
+ BUG_ON(!ni);
+ m = map_mft_record(ni);
+ if (IS_ERR(m)) {
+ err = PTR_ERR(m);
+ goto err_out;
+ }
+ ctx = get_attr_search_ctx(ni, m);
+ if (!ctx) {
+ err = -ENOMEM;
+ goto put_unm_err_out;
+ }
+ if (!lookup_attr(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx)) {
+ err = -EIO;
+ goto put_unm_err_out;
+ }
+ vi = (VOLUME_INFORMATION*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->data.resident.value_offset));
+ vol->vol_flags = vi->flags = flags;
+ flush_dcache_mft_record_page(ctx->ntfs_ino);
+ mark_mft_record_dirty(ctx->ntfs_ino);
+ put_attr_search_ctx(ctx);
+ unmap_mft_record(ni);
+done:
+ ntfs_debug("Done.");
+ return 0;
+put_unm_err_out:
+ if (ctx)
+ put_attr_search_ctx(ctx);
+ unmap_mft_record(ni);
+err_out:
+ ntfs_error(vol->sb, "Failed with error code %i.", -err);
+ return err;
+}
+
+/**
+ * ntfs_set_volume_flags - set bits in the volume information flags
+ * @vol: ntfs volume on which to modify the flags
+ * @flags: flags to set on the volume
+ *
+ * Set the bits in @flags in the volume information flags on the volume @vol.
+ *
+ * Return 0 on success and -errno on error.
+ */
+static inline int ntfs_set_volume_flags(ntfs_volume *vol, VOLUME_FLAGS flags)
+{
+ flags &= VOLUME_FLAGS_MASK;
+ return ntfs_write_volume_flags(vol, vol->vol_flags | flags);
+}
+
+/**
+ * ntfs_clear_volume_flags - clear bits in the volume information flags
+ * @vol: ntfs volume on which to modify the flags
+ * @flags: flags to clear on the volume
+ *
+ * Clear the bits in @flags in the volume information flags on the volume @vol.
+ *
+ * Return 0 on success and -errno on error.
+ */
+static inline int ntfs_clear_volume_flags(ntfs_volume *vol, VOLUME_FLAGS flags)
+{
+ flags &= VOLUME_FLAGS_MASK;
+ return ntfs_write_volume_flags(vol, vol->vol_flags & ~flags);
+}
+
+#endif /* NTFS_RW */
+
/**
* ntfs_remount - change the mount options of a mounted ntfs filesystem
* @sb: superblock of mounted ntfs filesystem
#else /* ! NTFS_RW */
/*
* For the read-write compiled driver, if we are remounting read-write,
- * make sure there aren't any volume errors and empty the lofgile.
+ * make sure there are no volume errors and that no unsupported volume
+ * flags are set. Also, empty the logfile journal as it would become
+ * stale as soon as something is written to the volume and mark the
+ * volume dirty so that chkdsk is run if the volume is not umounted
+ * cleanly.
+ *
+ * When remounting read-only, mark the volume clean if no volume errors
+ * have occured.
*/
if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) {
static const char *es = ". Cannot remount read-write.";
+ /* Remounting read-write. */
if (NVolErrors(vol)) {
ntfs_error(sb, "Volume has errors and is read-only%s",
es);
return -EROFS;
}
+ if (vol->vol_flags & VOLUME_IS_DIRTY) {
+ ntfs_error(sb, "Volume is dirty and read-only%s", es);
+ return -EROFS;
+ }
+ if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) {
+ ntfs_error(sb, "Volume has unsupported flags set and "
+ "is read-only%s", es);
+ return -EROFS;
+ }
+ if (ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) {
+ ntfs_error(sb, "Failed to set dirty bit in volume "
+ "information flags%s", es);
+ return -EROFS;
+ }
+#if 0
+ // TODO: Enable this code once we start modifying anything that
+ // is different between NTFS 1.2 and 3.x...
+ /* Set NT4 compatibility flag on newer NTFS version volumes. */
+ if ((vol->major_ver > 1)) {
+ if (ntfs_set_volume_flags(vol, VOLUME_MOUNTED_ON_NT4)) {
+ ntfs_error(sb, "Failed to set NT4 "
+ "compatibility flag%s", es);
+ NVolSetErrors(vol);
+ return -EROFS;
+ }
+ }
+#endif
if (!ntfs_empty_logfile(vol->logfile_ino)) {
ntfs_error(sb, "Failed to empty journal $LogFile%s",
es);
NVolSetErrors(vol);
return -EROFS;
}
+ } else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) {
+ /* Remounting read-only. */
+ if (!NVolErrors(vol)) {
+ if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY))
+ ntfs_warning(sb, "Failed to clear dirty bit "
+ "in volume information "
+ "flags. Run chkdsk.");
+ }
}
// TODO: For now we enforce no atime and dir atime updates as they are
// not implemented.
+ if ((sb->s_flags & MS_NOATIME) && !(*flags & MS_NOATIME))
+ ntfs_warning(sb, "Atime updates are not implemented yet. "
+ "Leaving them disabled.");
+ else if ((sb->s_flags & MS_NODIRATIME) && !(*flags & MS_NODIRATIME))
+ ntfs_warning(sb, "Directory atime updates are not implemented "
+ "yet. Leaving them disabled.");
*flags |= MS_NOATIME | MS_NODIRATIME;
#endif /* ! NTFS_RW */
/* The $MFTMirr, like the $MFT is multi sector transfer protected. */
NInoSetMstProtected(tmp_ni);
/*
- * Set up our little cheat allowing us to reuse the async io
+ * Set up our little cheat allowing us to reuse the async read io
* completion handler for directories.
*/
tmp_ni->itype.index.block_size = vol->mft_record_size;
}
/*
* The upcase size must not be above 64k Unicode characters, must not
- * be zero and must be a multiple of sizeof(uchar_t).
+ * be zero and must be a multiple of sizeof(ntfschar).
*/
- if (!ino->i_size || ino->i_size & (sizeof(uchar_t) - 1) ||
- ino->i_size > 64ULL * 1024 * sizeof(uchar_t))
+ if (!ino->i_size || ino->i_size & (sizeof(ntfschar) - 1) ||
+ ino->i_size > 64ULL * 1024 * sizeof(ntfschar))
goto iput_upcase_failed;
- vol->upcase = (uchar_t*)ntfs_malloc_nofs(ino->i_size);
+ vol->upcase = (ntfschar*)ntfs_malloc_nofs(ino->i_size);
if (!vol->upcase)
goto iput_upcase_failed;
index = 0;
}
vol->upcase_len = ino->i_size >> UCHAR_T_SIZE_BITS;
ntfs_debug("Read %llu bytes from $UpCase (expected %u bytes).",
- ino->i_size, 64 * 1024 * sizeof(uchar_t));
+ ino->i_size, 64 * 1024 * sizeof(ntfschar));
iput(ino);
down(&ntfs_lock);
if (!default_upcase) {
le32_to_cpu(ctx->attr->data.resident.value_length) >
(u8*)ctx->attr + le32_to_cpu(ctx->attr->length))
goto err_put_vol;
- /* Setup volume flags and version. */
+ /* Copy the volume flags and version to the ntfs_volume structure. */
vol->vol_flags = vi->flags;
vol->major_ver = vi->major_ver;
vol->minor_ver = vi->minor_ver;
printk(KERN_INFO "NTFS volume version %i.%i.\n", vol->major_ver,
vol->minor_ver);
#ifdef NTFS_RW
+ /* Make sure that no unsupported volume flags are set. */
+ if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) {
+ static const char *es1a = "Volume is dirty";
+ static const char *es1b = "Volume has unsupported flags set";
+ static const char *es2 = ". Run chkdsk and mount in Windows.";
+ const char *es1;
+
+ es1 = vol->vol_flags & VOLUME_IS_DIRTY ? es1a : es1b;
+ /* If a read-write mount, convert it to a read-only mount. */
+ if (!(sb->s_flags & MS_RDONLY)) {
+ 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_vol_err_out;
+ }
+ sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
+ ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
+ } else
+ ntfs_warning(sb, "%s. Will not be able to remount "
+ "read-write%s", es1, es2);
+ /*
+ * Do not set NVolErrors() because ntfs_remount() re-checks the
+ * flags which we need to do in case any flags have changed.
+ */
+ }
/*
* Get the inode for the logfile, check it and determine if the volume
* was shutdown cleanly.
*/
if (!load_and_check_logfile(vol) ||
!ntfs_is_logfile_clean(vol->logfile_ino)) {
- static const char *es1 = "Failed to load $LogFile";
- static const char *es2 = "$LogFile is not clean";
- static const char *es3 = ". Mount in Windows.";
+ static const char *es1a = "Failed to load $LogFile";
+ static const char *es1b = "$LogFile is not clean";
+ static const char *es2 = ". Mount in Windows.";
+ const char *es1;
+ es1 = !vol->logfile_ino ? es1a : es1b;
/* If a read-write mount, convert it to a read-only mount. */
if (!(sb->s_flags & MS_RDONLY)) {
if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO |
ntfs_error(sb, "%s and neither on_errors="
"continue nor on_errors="
"remount-ro was specified%s",
- !vol->logfile_ino ? es1 : es2,
- es3);
+ es1, es2);
goto iput_logfile_err_out;
}
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
- ntfs_error(sb, "%s. Mounting read-only%s",
- !vol->logfile_ino ? es1 : es2, es3);
+ ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
} else
ntfs_warning(sb, "%s. Will not be able to remount "
- "read-write%s",
- !vol->logfile_ino ? es1 : es2, es3);
+ "read-write%s", es1, es2);
/* This will prevent a read-write remount. */
NVolSetErrors(vol);
- /* If a read-write mount, empty the logfile. */
- } else if (!(sb->s_flags & MS_RDONLY) &&
+ }
+ /* If (still) a read-write mount, mark the volume dirty. */
+ if (!(sb->s_flags & MS_RDONLY) &&
+ ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) {
+ static const char *es1 = "Failed to set dirty bit in volume "
+ "information flags";
+ static const char *es2 = ". Run chkdsk.";
+
+ /* 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;
+ }
+ ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
+ sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
+ /*
+ * Do not set NVolErrors() because ntfs_remount() might manage
+ * to set the dirty flag in which case all would be well.
+ */
+ }
+#if 0
+ // TODO: Enable this code once we start modifying anything that is
+ // different between NTFS 1.2 and 3.x...
+ /*
+ * If (still) a read-write mount, set the NT4 compatibility flag on
+ * newer NTFS version volumes.
+ */
+ if (!(sb->s_flags & MS_RDONLY) && (vol->major_ver > 1) &&
+ ntfs_set_volume_flags(vol, VOLUME_MOUNTED_ON_NT4)) {
+ static const char *es1 = "Failed to set NT4 compatibility flag";
+ static const char *es2 = ". Run chkdsk.";
+
+ /* 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;
+ }
+ ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
+ sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
+ NVolSetErrors(vol);
+ }
+#endif
+ /* If (still) a read-write mount, empty the logfile. */
+ 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.";
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. */
+ sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
NVolSetErrors(vol);
}
-#endif
+#endif /* NTFS_RW */
/*
* Get the inode for the attribute definitions file and parse the
* attribute definitions.
#ifdef NTFS_RW
if (vol->logfile_ino)
iput(vol->logfile_ino);
+iput_vol_err_out:
#endif /* NTFS_RW */
iput(vol->vol_ino);
iput_lcnbmp_err_out:
/**
* ntfs_put_super - called by the vfs to unmount a volume
- * @vfs_sb: vfs superblock of volume to unmount
+ * @sb: vfs superblock of volume to unmount
*
* ntfs_put_super() is called by the VFS (from fs/super.c::do_umount()) when
* the volume is being unmounted (umount system call has been invoked) and it
* releases all inodes and memory belonging to the NTFS specific part of the
* super block.
*/
-static void ntfs_put_super(struct super_block *vfs_sb)
+static void ntfs_put_super(struct super_block *sb)
{
- ntfs_volume *vol = NTFS_SB(vfs_sb);
+ ntfs_volume *vol = NTFS_SB(sb);
ntfs_debug("Entering.");
+#ifdef NTFS_RW
+ /*
+ * Commit all inodes while they are still open in case some of them
+ * cause others to be dirtied.
+ */
+ ntfs_commit_inode(vol->vol_ino);
+
+ /* NTFS 3.0+ specific. */
+ if (vol->major_ver >= 3) {
+ if (vol->secure_ino)
+ ntfs_commit_inode(vol->secure_ino);
+ }
+
+ ntfs_commit_inode(vol->root_ino);
+
+ down_write(&vol->lcnbmp_lock);
+ ntfs_commit_inode(vol->lcnbmp_ino);
+ up_write(&vol->lcnbmp_lock);
+
+ down_write(&vol->mftbmp_lock);
+ ntfs_commit_inode(vol->mftbmp_ino);
+ up_write(&vol->mftbmp_lock);
+
+ if (vol->logfile_ino)
+ ntfs_commit_inode(vol->logfile_ino);
+
+ if (vol->mftmirr_ino)
+ ntfs_commit_inode(vol->mftmirr_ino);
+ ntfs_commit_inode(vol->mft_ino);
+
+ /*
+ * If a read-write mount and no volume errors have occured, mark the
+ * volume clean. Also, re-commit all affected inodes.
+ */
+ if (!(sb->s_flags & MS_RDONLY)) {
+ if (!NVolErrors(vol)) {
+ if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY))
+ ntfs_warning(sb, "Failed to clear dirty bit "
+ "in volume information "
+ "flags. Run chkdsk.");
+ ntfs_commit_inode(vol->vol_ino);
+ ntfs_commit_inode(vol->root_ino);
+ if (vol->mftmirr_ino)
+ ntfs_commit_inode(vol->mftmirr_ino);
+ ntfs_commit_inode(vol->mft_ino);
+ } else {
+ ntfs_warning(sb, "Volume has errors. Leaving volume "
+ "marked dirty. Run chkdsk.");
+ }
+ }
+#endif /* NTFS_RW */
iput(vol->vol_ino);
vol->vol_ino = NULL;
iput(vol->logfile_ino);
vol->logfile_ino = NULL;
}
-
if (vol->mftmirr_ino) {
+ /* Re-commit the mft mirror and mft just in case. */
+ ntfs_commit_inode(vol->mftmirr_ino);
+ ntfs_commit_inode(vol->mft_ino);
iput(vol->mftmirr_ino);
vol->mftmirr_ino = NULL;
}
+ /*
+ * If any dirty inodes are left, throw away all mft data page cache
+ * pages to allow a clean umount. This should never happen any more
+ * due to mft.c::ntfs_mft_writepage() cleaning all the dirty pages as
+ * the underlying mft records are written out and cleaned. If it does,
+ * happen anyway, we want to know...
+ */
+ ntfs_commit_inode(vol->mft_ino);
+ write_inode_now(vol->mft_ino, 1);
+ if (!list_empty(&sb->s_dirty)) {
+ const char *s1, *s2;
+
+ down(&vol->mft_ino->i_sem);
+ truncate_inode_pages(vol->mft_ino->i_mapping, 0);
+ up(&vol->mft_ino->i_sem);
+ write_inode_now(vol->mft_ino, 1);
+ if (!list_empty(&sb->s_dirty)) {
+ static const char *_s1 = "inodes";
+ static const char *_s2 = "";
+ s1 = _s1;
+ s2 = _s2;
+ } else {
+ static const char *_s1 = "mft pages";
+ static const char *_s2 = "They have been thrown "
+ "away. ";
+ s1 = _s1;
+ s2 = _s2;
+ }
+ ntfs_error(sb, "Dirty %s found at umount time. %sYou should "
+ "run chkdsk. Please email "
+ "linux-ntfs-dev@lists.sourceforge.net and say "
+ "that you saw this message. Thank you.", s1,
+ s2);
+ }
#endif /* NTFS_RW */
iput(vol->mft_ino);
vol->upcase_len = 0;
/*
* Decrease the number of mounts and destroy the global default upcase
- * table if necessary. Also decrease the number of upcase users if we
+ * table if necessary. Also decrease the number of upcase users if we
* are a user.
*/
down(&ntfs_lock);
unload_nls(vol->nls_map);
vol->nls_map = NULL;
}
- vfs_sb->s_fs_info = NULL;
+ sb->s_fs_info = NULL;
kfree(vol);
return;
}
return 0;
}
-/**
- * Super operations for mount time when we don't have enough setup to use the
- * proper functions.
- */
-struct super_operations ntfs_mount_sops = {
- .alloc_inode = ntfs_alloc_big_inode, /* VFS: Allocate new inode. */
- .destroy_inode = ntfs_destroy_big_inode, /* VFS: Deallocate inode. */
- .read_inode = ntfs_read_inode_mount, /* VFS: Load inode from disk,
- called from iget(). */
- .clear_inode = ntfs_clear_big_inode, /* VFS: Called when inode is
- removed from memory. */
-};
-
/**
* The complete super operations.
*/
#ifdef NTFS_RW
//.dirty_inode = NULL, /* VFS: Called from
// __mark_inode_dirty(). */
- //.write_inode = NULL, /* VFS: Write dirty inode to
- // disk. */
+ .write_inode = ntfs_write_inode, /* VFS: Write dirty inode to
+ disk. */
//.drop_inode = NULL, /* VFS: Called just after the
// inode reference count has
// been decreased to zero.
#ifndef NTFS_RW
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
#else
- // TODO: For now we enforce no atime and dir atime updates as they are
- // not implemented.
+ if (!(sb->s_flags & MS_NOATIME))
+ ntfs_warning(sb, "Atime updates are not implemented yet. "
+ "Disabling them.");
+ else if (!(sb->s_flags & MS_NODIRATIME))
+ ntfs_warning(sb, "Directory atime updates are not implemented "
+ "yet. Disabling them.");
sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
#endif
/* Allocate a new ntfs_volume and place it in sb->s_fs_info. */
* the inode for $MFT which is sufficient to allow our normal inode
* operations and associated address space operations to function.
*/
- /*
- * Poison vol->mft_ino so we know whether iget() called into our
- * ntfs_read_inode_mount() method.
- */
-#define OGIN ((struct inode*)n2p(le32_to_cpu(0x4e49474f))) /* OGIN */
- vol->mft_ino = OGIN;
- sb->s_op = &ntfs_mount_sops;
- tmp_ino = iget(vol->sb, FILE_MFT);
- if (!tmp_ino || tmp_ino != vol->mft_ino || is_bad_inode(tmp_ino)) {
+ sb->s_op = &ntfs_sops;
+ tmp_ino = new_inode(sb);
+ if (!tmp_ino) {
+ if (!silent)
+ ntfs_error(sb, "Failed to load essential metadata.");
+ goto err_out_now;
+ }
+ tmp_ino->i_ino = FILE_MFT;
+ insert_inode_hash(tmp_ino);
+ if (ntfs_read_inode_mount(tmp_ino) < 0) {
if (!silent)
ntfs_error(sb, "Failed to load essential metadata.");
- if (tmp_ino && vol->mft_ino == OGIN)
- ntfs_error(sb, "BUG: iget() did not call "
- "ntfs_read_inode_mount() method!\n");
- if (!tmp_ino)
- goto cond_iput_mft_ino_err_out_now;
goto iput_tmp_ino_err_out_now;
}
- /*
- * Note: sb->s_op has already been set to &ntfs_sops by our specialized
- * ntfs_read_inode_mount() method when it was invoked by iget().
- */
down(&ntfs_lock);
/*
* The current mount is a compression user if the cluster size is
up(&ntfs_lock);
iput_tmp_ino_err_out_now:
iput(tmp_ino);
-cond_iput_mft_ino_err_out_now:
- if (vol->mft_ino && vol->mft_ino != OGIN && vol->mft_ino != tmp_ino) {
+ if (vol->mft_ino && vol->mft_ino != tmp_ino) {
iput(vol->mft_ino);
vol->mft_ino = NULL;
}
-#undef OGIN
/*
* This is needed to get ntfs_clear_extent_inode() called for each
* inode we have ever called ntfs_iget()/iput() on, otherwise we A)
}
ntfs_name_cache = kmem_cache_create(ntfs_name_cache_name,
- (NTFS_MAX_NAME_LEN+1) * sizeof(uchar_t), 0,
+ (NTFS_MAX_NAME_LEN+1) * sizeof(ntfschar), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!ntfs_name_cache) {
printk(KERN_CRIT "NTFS: Failed to create %s!\n",