Hi Linus, please pull from
rsync://rsync.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs-2.6.git/HEAD
This is the next NTFS update containing a ton of bug fixes several of
which fix bugs people actually hit in the big bad world...
Please apply. Thanks!
I am sending the changesets as actual patches generated using git
format-patch for non-git users in follow up emails (in reply to this one).
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
NTFS: Support more clean journal ($LogFile) states.
- Support journals ($LogFile) which have been modified by chkdsk. This
means users can boot into Windows after we marked the volume dirty.
The Windows boot will run chkdsk and then reboot. The user can then
immediately boot into Linux rather than having to do a full Windows
boot first before rebooting into Linux and we will recognize such a
journal and empty it as it is clean by definition.
- Support journals ($LogFile) with only one restart page as well as
journals with two different restart pages. We sanity check both and
either use the only sane one or the more recent one of the two in the
case that both are valid.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 13 +++
fs/ntfs/Makefile | 2
fs/ntfs/logfile.c | 255 +++++++++++++++++++++++++++++------------------------
fs/ntfs/logfile.h | 8 +-
fs/ntfs/super.c | 16 ++-
5 files changed, 169 insertions(+), 125 deletions(-)
e7a1033b946f4f2622f2b338ab107f559aad542c
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -22,6 +22,19 @@ ToDo/Notes:
- Enable the code for setting the NT4 compatibility flag when we start
making NTFS 1.2 specific modifications.
+2.1.24-WIP
+
+ - Support journals ($LogFile) which have been modified by chkdsk. This
+ means users can boot into Windows after we marked the volume dirty.
+ The Windows boot will run chkdsk and then reboot. The user can then
+ immediately boot into Linux rather than having to do a full Windows
+ boot first before rebooting into Linux and we will recognize such a
+ journal and empty it as it is clean by definition.
+ - Support journals ($LogFile) with only one restart page as well as
+ journals with two different restart pages. We sanity check both and
+ either use the only sane one or the more recent one of the two in the
+ case that both are valid.
+
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile
--- a/fs/ntfs/Makefile
+++ b/fs/ntfs/Makefile
@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o c
index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
unistr.o upcase.o
-EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.23\"
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.24-WIP\"
ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c
--- a/fs/ntfs/logfile.c
+++ b/fs/ntfs/logfile.c
@@ -121,7 +121,7 @@ static BOOL ntfs_check_restart_page_head
*/
if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
ntfs_error(vi->i_sb, "$LogFile restart page is not modified "
- "chkdsk but a chkdsk LSN is specified.");
+ "by chkdsk but a chkdsk LSN is specified.");
return FALSE;
}
ntfs_debug("Done.");
@@ -312,10 +312,12 @@ err_out:
* @vi: $LogFile inode to which the restart page belongs
* @rp: restart page to check
* @pos: position in @vi at which the restart page resides
- * @wrp: copy of the multi sector transfer deprotected restart page
+ * @wrp: [OUT] copy of the multi sector transfer deprotected restart page
+ * @lsn: [OUT] set to the current logfile lsn on success
*
- * Check the restart page @rp for consistency and return TRUE if it is
- * consistent and FALSE otherwise.
+ * Check the restart page @rp for consistency and return 0 if it is consistent
+ * and -errno otherwise. The restart page may have been modified by chkdsk in
+ * which case its magic is CHKD instead of RSTR.
*
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
* require the full restart page.
@@ -323,25 +325,33 @@ err_out:
* If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
* copy of the complete multi sector transfer deprotected page. On failure,
* *@wrp is undefined.
+ *
+ * Simillarly, if @lsn is not NULL, on succes *@lsn will be set to the current
+ * logfile lsn according to this restart page. On failure, *@lsn is undefined.
+ *
+ * The following error codes are defined:
+ * -EINVAL - The restart page is inconsistent.
+ * -ENOMEM - Not enough memory to load the restart page.
+ * -EIO - Failed to reading from $LogFile.
*/
-static BOOL ntfs_check_and_load_restart_page(struct inode *vi,
- RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp)
+static int ntfs_check_and_load_restart_page(struct inode *vi,
+ RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
+ LSN *lsn)
{
RESTART_AREA *ra;
RESTART_PAGE_HEADER *trp;
- int size;
- BOOL ret;
+ int size, err;
ntfs_debug("Entering.");
/* Check the restart page header for consistency. */
if (!ntfs_check_restart_page_header(vi, rp, pos)) {
/* Error output already done inside the function. */
- return FALSE;
+ return -EINVAL;
}
/* Check the restart area for consistency. */
if (!ntfs_check_restart_area(vi, rp)) {
/* Error output already done inside the function. */
- return FALSE;
+ return -EINVAL;
}
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
/*
@@ -352,7 +362,7 @@ static BOOL ntfs_check_and_load_restart_
if (!trp) {
ntfs_error(vi->i_sb, "Failed to allocate memory for $LogFile "
"restart page buffer.");
- return FALSE;
+ return -ENOMEM;
}
/*
* Read the whole of the restart page into the buffer. If it fits
@@ -379,6 +389,9 @@ static BOOL ntfs_check_and_load_restart_
if (IS_ERR(page)) {
ntfs_error(vi->i_sb, "Error mapping $LogFile "
"page (index %lu).", idx);
+ err = PTR_ERR(page);
+ if (err != -EIO && err != -ENOMEM)
+ err = -EIO;
goto err_out;
}
size = min_t(int, to_read, PAGE_CACHE_SIZE);
@@ -392,29 +405,57 @@ static BOOL ntfs_check_and_load_restart_
/* Perform the multi sector transfer deprotection on the buffer. */
if (post_read_mst_fixup((NTFS_RECORD*)trp,
le32_to_cpu(rp->system_page_size))) {
- ntfs_error(vi->i_sb, "Multi sector transfer error detected in "
- "$LogFile restart page.");
- goto err_out;
- }
- /* Check the log client records for consistency. */
- ret = ntfs_check_log_client_array(vi, trp);
- if (ret && wrp)
- *wrp = trp;
- else
- ntfs_free(trp);
+ /*
+ * A multi sector tranfer error was detected. We only need to
+ * abort if the restart page contents exceed the multi sector
+ * transfer fixup of the first sector.
+ */
+ if (le16_to_cpu(rp->restart_area_offset) +
+ le16_to_cpu(ra->restart_area_length) >
+ NTFS_BLOCK_SIZE - sizeof(u16)) {
+ ntfs_error(vi->i_sb, "Multi sector transfer error "
+ "detected in $LogFile restart page.");
+ err = -EINVAL;
+ goto err_out;
+ }
+ }
+ /*
+ * If the restart page is modified by chkdsk or there are no active
+ * logfile clients, the logfile is consistent. Otherwise, need to
+ * check the log client records for consistency, too.
+ */
+ err = 0;
+ if (ntfs_is_rstr_record(rp->magic) &&
+ ra->client_in_use_list != LOGFILE_NO_CLIENT) {
+ if (!ntfs_check_log_client_array(vi, trp)) {
+ err = -EINVAL;
+ goto err_out;
+ }
+ }
+ if (lsn) {
+ if (ntfs_is_rstr_record(rp->magic))
+ *lsn = sle64_to_cpu(ra->current_lsn);
+ else /* if (ntfs_is_chkd_record(rp->magic)) */
+ *lsn = sle64_to_cpu(rp->chkdsk_lsn);
+ }
ntfs_debug("Done.");
- return ret;
+ if (wrp)
+ *wrp = trp;
+ else {
err_out:
- ntfs_free(trp);
- return FALSE;
+ ntfs_free(trp);
+ }
+ return err;
}
/**
* ntfs_check_logfile - check the journal for consistency
* @log_vi: struct inode of loaded journal $LogFile to check
+ * @rp: [OUT] on success this is a copy of the current restart page
*
* Check the $LogFile journal for consistency and return TRUE if it is
- * consistent and FALSE if not.
+ * consistent and FALSE if not. On success, the current restart page is
+ * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it.
*
* At present we only check the two restart pages and ignore the log record
* pages.
@@ -424,19 +465,18 @@ err_out:
* if the $LogFile was created on a system with a different page size to ours
* yet and mst deprotection would fail if our page size is smaller.
*/
-BOOL ntfs_check_logfile(struct inode *log_vi)
+BOOL ntfs_check_logfile(struct inode *log_vi, RESTART_PAGE_HEADER **rp)
{
- s64 size, pos, rstr1_pos, rstr2_pos;
+ s64 size, pos;
+ LSN rstr1_lsn, rstr2_lsn;
ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
struct address_space *mapping = log_vi->i_mapping;
struct page *page = NULL;
u8 *kaddr = NULL;
RESTART_PAGE_HEADER *rstr1_ph = NULL;
RESTART_PAGE_HEADER *rstr2_ph = NULL;
- int log_page_size, log_page_mask, ofs;
+ int log_page_size, log_page_mask, err;
BOOL logfile_is_empty = TRUE;
- BOOL rstr1_found = FALSE;
- BOOL rstr2_found = FALSE;
u8 log_page_bits;
ntfs_debug("Entering.");
@@ -491,7 +531,7 @@ BOOL ntfs_check_logfile(struct inode *lo
if (IS_ERR(page)) {
ntfs_error(vol->sb, "Error mapping $LogFile "
"page (index %lu).", idx);
- return FALSE;
+ goto err_out;
}
}
kaddr = (u8*)page_address(page) + (pos & ~PAGE_CACHE_MASK);
@@ -510,99 +550,95 @@ BOOL ntfs_check_logfile(struct inode *lo
*/
if (ntfs_is_rcrd_recordp((le32*)kaddr))
break;
- /*
- * A modified by chkdsk restart page means we cannot handle
- * this log file.
- */
- if (ntfs_is_chkd_recordp((le32*)kaddr)) {
- ntfs_error(vol->sb, "$LogFile has been modified by "
- "chkdsk. Mount this volume in "
- "Windows.");
- goto err_out;
- }
- /* If not a restart page, continue. */
- if (!ntfs_is_rstr_recordp((le32*)kaddr)) {
- /* Skip to the minimum page size for the next one. */
+ /* If not a (modified by chkdsk) restart page, continue. */
+ if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
+ !ntfs_is_chkd_recordp((le32*)kaddr)) {
if (!pos)
pos = NTFS_BLOCK_SIZE >> 1;
continue;
}
- /* We now know we have a restart page. */
- if (!pos) {
- rstr1_found = TRUE;
- rstr1_pos = pos;
- } else {
- if (rstr2_found) {
- ntfs_error(vol->sb, "Found more than two "
- "restart pages in $LogFile.");
- goto err_out;
- }
- rstr2_found = TRUE;
- rstr2_pos = pos;
- }
/*
- * Check the restart page for consistency and get a copy of the
- * complete multi sector transfer deprotected restart page.
+ * Check the (modified by chkdsk) restart page for consistency
+ * and get a copy of the complete multi sector transfer
+ * deprotected restart page.
*/
- if (!ntfs_check_and_load_restart_page(log_vi,
+ err = ntfs_check_and_load_restart_page(log_vi,
(RESTART_PAGE_HEADER*)kaddr, pos,
- !pos ? &rstr1_ph : &rstr2_ph)) {
- /* Error output already done inside the function. */
- goto err_out;
+ !rstr1_ph ? &rstr1_ph : &rstr2_ph,
+ !rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
+ if (!err) {
+ /*
+ * If we have now found the first (modified by chkdsk)
+ * restart page, continue looking for the second one.
+ */
+ if (!pos) {
+ pos = NTFS_BLOCK_SIZE >> 1;
+ continue;
+ }
+ /*
+ * We have now found the second (modified by chkdsk)
+ * restart page, so we can stop looking.
+ */
+ break;
}
/*
- * We have a valid restart page. The next one must be after
- * a whole system page size as specified by the valid restart
- * page.
+ * Error output already done inside the function. Note, we do
+ * not abort if the restart page was invalid as we might still
+ * find a valid one further in the file.
*/
+ if (err != -EINVAL) {
+ ntfs_unmap_page(page);
+ goto err_out;
+ }
+ /* Continue looking. */
if (!pos)
- pos = le32_to_cpu(rstr1_ph->system_page_size) >> 1;
+ pos = NTFS_BLOCK_SIZE >> 1;
}
- if (page) {
+ if (page)
ntfs_unmap_page(page);
- page = NULL;
- }
if (logfile_is_empty) {
NVolSetLogFileEmpty(vol);
is_empty:
ntfs_debug("Done. ($LogFile is empty.)");
return TRUE;
}
- if (!rstr1_found || !rstr2_found) {
- ntfs_error(vol->sb, "Did not find two restart pages in "
- "$LogFile.");
- goto err_out;
- }
- /*
- * The two restart areas must be identical except for the update
- * sequence number.
- */
- ofs = le16_to_cpu(rstr1_ph->usa_ofs);
- if (memcmp(rstr1_ph, rstr2_ph, ofs) || (ofs += sizeof(u16),
- memcmp((u8*)rstr1_ph + ofs, (u8*)rstr2_ph + ofs,
- le32_to_cpu(rstr1_ph->system_page_size) - ofs))) {
- ntfs_error(vol->sb, "The two restart pages in $LogFile do not "
- "match.");
- goto err_out;
+ if (!rstr1_ph) {
+ BUG_ON(rstr2_ph);
+ ntfs_error(vol->sb, "Did not find any restart pages in "
+ "$LogFile and it was not empty.");
+ return FALSE;
+ }
+ /* If both restart pages were found, use the more recent one. */
+ if (rstr2_ph) {
+ /*
+ * If the second restart area is more recent, switch to it.
+ * Otherwise just throw it away.
+ */
+ if (rstr2_lsn > rstr1_lsn) {
+ ntfs_free(rstr1_ph);
+ rstr1_ph = rstr2_ph;
+ /* rstr1_lsn = rstr2_lsn; */
+ } else
+ ntfs_free(rstr2_ph);
+ rstr2_ph = NULL;
}
- ntfs_free(rstr1_ph);
- ntfs_free(rstr2_ph);
/* All consistency checks passed. */
+ if (rp)
+ *rp = rstr1_ph;
+ else
+ ntfs_free(rstr1_ph);
ntfs_debug("Done.");
return TRUE;
err_out:
- if (page)
- ntfs_unmap_page(page);
if (rstr1_ph)
ntfs_free(rstr1_ph);
- if (rstr2_ph)
- ntfs_free(rstr2_ph);
return FALSE;
}
/**
* ntfs_is_logfile_clean - check in the journal if the volume is clean
* @log_vi: struct inode of loaded journal $LogFile to check
+ * @rp: copy of the current restart page
*
* Analyze the $LogFile journal and return TRUE if it indicates the volume was
* shutdown cleanly and FALSE if not.
@@ -619,11 +655,9 @@ err_out:
* is empty this function requires that NVolLogFileEmpty() is true otherwise an
* empty volume will be reported as dirty.
*/
-BOOL ntfs_is_logfile_clean(struct inode *log_vi)
+BOOL ntfs_is_logfile_clean(struct inode *log_vi, const RESTART_PAGE_HEADER *rp)
{
ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
- struct page *page;
- RESTART_PAGE_HEADER *rp;
RESTART_AREA *ra;
ntfs_debug("Entering.");
@@ -632,23 +666,14 @@ BOOL ntfs_is_logfile_clean(struct inode
ntfs_debug("Done. ($LogFile is empty.)");
return TRUE;
}
- /*
- * Read the first restart page. It will be possibly incomplete and
- * will not be multi sector transfer deprotected but we only need the
- * first NTFS_BLOCK_SIZE bytes so it does not matter.
- */
- page = ntfs_map_page(log_vi->i_mapping, 0);
- if (IS_ERR(page)) {
- ntfs_error(vol->sb, "Error mapping $LogFile page (index 0).");
- return FALSE;
- }
- rp = (RESTART_PAGE_HEADER*)page_address(page);
- if (!ntfs_is_rstr_record(rp->magic)) {
- ntfs_error(vol->sb, "No restart page found at offset zero in "
- "$LogFile. This is probably a bug in that "
- "the $LogFile should have been consistency "
- "checked before calling this function.");
- goto err_out;
+ BUG_ON(!rp);
+ if (!ntfs_is_rstr_record(rp->magic) &&
+ !ntfs_is_chkd_record(rp->magic)) {
+ ntfs_error(vol->sb, "Restart page buffer is invalid. This is "
+ "probably a bug in that the $LogFile should "
+ "have been consistency checked before calling "
+ "this function.");
+ return FALSE;
}
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
/*
@@ -659,15 +684,11 @@ BOOL ntfs_is_logfile_clean(struct inode
if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
!(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
ntfs_debug("Done. $LogFile indicates a dirty shutdown.");
- goto err_out;
+ return FALSE;
}
- ntfs_unmap_page(page);
/* $LogFile indicates a clean shutdown. */
ntfs_debug("Done. $LogFile indicates a clean shutdown.");
return TRUE;
-err_out:
- ntfs_unmap_page(page);
- return FALSE;
}
/**
diff --git a/fs/ntfs/logfile.h b/fs/ntfs/logfile.h
--- a/fs/ntfs/logfile.h
+++ b/fs/ntfs/logfile.h
@@ -2,7 +2,7 @@
* logfile.h - Defines for NTFS kernel journal ($LogFile) handling. Part of
* the Linux-NTFS project.
*
- * Copyright (c) 2000-2004 Anton Altaparmakov
+ * Copyright (c) 2000-2005 Anton Altaparmakov
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
@@ -296,9 +296,11 @@ typedef struct {
/* sizeof() = 160 (0xa0) bytes */
} __attribute__ ((__packed__)) LOG_CLIENT_RECORD;
-extern BOOL ntfs_check_logfile(struct inode *log_vi);
+extern BOOL ntfs_check_logfile(struct inode *log_vi,
+ RESTART_PAGE_HEADER **rp);
-extern BOOL ntfs_is_logfile_clean(struct inode *log_vi);
+extern BOOL ntfs_is_logfile_clean(struct inode *log_vi,
+ const RESTART_PAGE_HEADER *rp);
extern BOOL ntfs_empty_logfile(struct inode *log_vi);
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1133,7 +1133,8 @@ mft_unmap_out:
*
* Return TRUE on success or FALSE on error.
*/
-static BOOL load_and_check_logfile(ntfs_volume *vol)
+static BOOL load_and_check_logfile(ntfs_volume *vol,
+ RESTART_PAGE_HEADER **rp)
{
struct inode *tmp_ino;
@@ -1145,7 +1146,7 @@ static BOOL load_and_check_logfile(ntfs_
/* Caller will display error message. */
return FALSE;
}
- if (!ntfs_check_logfile(tmp_ino)) {
+ if (!ntfs_check_logfile(tmp_ino, rp)) {
iput(tmp_ino);
/* ntfs_check_logfile() will have displayed error output. */
return FALSE;
@@ -1687,6 +1688,7 @@ static BOOL load_system_files(ntfs_volum
struct super_block *sb = vol->sb;
MFT_RECORD *m;
VOLUME_INFORMATION *vi;
+ RESTART_PAGE_HEADER *rp;
ntfs_attr_search_ctx *ctx;
#ifdef NTFS_RW
int err;
@@ -1841,8 +1843,9 @@ get_ctx_vol_failed:
* 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)) {
+ rp = NULL;
+ if (!load_and_check_logfile(vol, &rp) ||
+ !ntfs_is_logfile_clean(vol->logfile_ino, rp)) {
static const char *es1a = "Failed to load $LogFile";
static const char *es1b = "$LogFile is not clean";
static const char *es2 = ". Mount in Windows.";
@@ -1857,6 +1860,10 @@ get_ctx_vol_failed:
"continue nor on_errors="
"remount-ro was specified%s",
es1, es2);
+ if (vol->logfile_ino) {
+ BUG_ON(!rp);
+ ntfs_free(rp);
+ }
goto iput_logfile_err_out;
}
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
@@ -1867,6 +1874,7 @@ get_ctx_vol_failed:
/* This will prevent a read-write remount. */
NVolSetErrors(vol);
}
+ ntfs_free(rp);
#endif /* NTFS_RW */
/* Get the root directory inode so we can do path lookups. */
vol->root_ino = ntfs_iget(sb, FILE_root);
NTFS: Allow highmem kmalloc() in ntfs_malloc_nofs() and add _nofail() version.
- Modify fs/ntfs/malloc.h::ntfs_malloc_nofs() to do the kmalloc() based
allocations with __GFP_HIGHMEM, analogous to how the vmalloc() based
allocations are done.
- Add fs/ntfs/malloc.h::ntfs_malloc_nofs_nofail() which is analogous to
ntfs_malloc_nofs() but it performs allocations with __GFP_NOFAIL and
hence cannot fail.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 6 ++++++
fs/ntfs/malloc.h | 48 ++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 48 insertions(+), 6 deletions(-)
06d0e3cf3d527f927681773c6ffbe697ccc5db7f
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -34,6 +34,12 @@ ToDo/Notes:
journals with two different restart pages. We sanity check both and
either use the only sane one or the more recent one of the two in the
case that both are valid.
+ - Modify fs/ntfs/malloc.h::ntfs_malloc_nofs() to do the kmalloc() based
+ allocations with __GFP_HIGHMEM, analogous to how the vmalloc() based
+ allocations are done.
+ - Add fs/ntfs/malloc.h::ntfs_malloc_nofs_nofail() which is analogous to
+ ntfs_malloc_nofs() but it performs allocations with __GFP_NOFAIL and
+ hence cannot fail.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/malloc.h b/fs/ntfs/malloc.h
--- a/fs/ntfs/malloc.h
+++ b/fs/ntfs/malloc.h
@@ -27,27 +27,63 @@
#include <linux/highmem.h>
/**
- * ntfs_malloc_nofs - allocate memory in multiples of pages
- * @size number of bytes to allocate
+ * __ntfs_malloc - allocate memory in multiples of pages
+ * @size: number of bytes to allocate
+ * @gfp_mask: extra flags for the allocator
+ *
+ * Internal function. You probably want ntfs_malloc_nofs()...
*
* Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
* returns a pointer to the allocated memory.
*
* If there was insufficient memory to complete the request, return NULL.
+ * Depending on @gfp_mask the allocation may be guaranteed to succeed.
*/
-static inline void *ntfs_malloc_nofs(unsigned long size)
+static inline void *__ntfs_malloc(unsigned long size,
+ unsigned int __nocast gfp_mask)
{
if (likely(size <= PAGE_SIZE)) {
BUG_ON(!size);
/* kmalloc() has per-CPU caches so is faster for now. */
- return kmalloc(PAGE_SIZE, GFP_NOFS);
- /* return (void *)__get_free_page(GFP_NOFS | __GFP_HIGHMEM); */
+ return kmalloc(PAGE_SIZE, gfp_mask);
+ /* return (void *)__get_free_page(gfp_mask); */
}
if (likely(size >> PAGE_SHIFT < num_physpages))
- return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
+ return __vmalloc(size, gfp_mask, PAGE_KERNEL);
return NULL;
}
+/**
+ * ntfs_malloc_nofs - allocate memory in multiples of pages
+ * @size: number of bytes to allocate
+ *
+ * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
+ * returns a pointer to the allocated memory.
+ *
+ * If there was insufficient memory to complete the request, return NULL.
+ */
+static inline void *ntfs_malloc_nofs(unsigned long size)
+{
+ return __ntfs_malloc(size, GFP_NOFS | __GFP_HIGHMEM);
+}
+
+/**
+ * ntfs_malloc_nofs_nofail - allocate memory in multiples of pages
+ * @size: number of bytes to allocate
+ *
+ * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
+ * returns a pointer to the allocated memory.
+ *
+ * This function guarantees that the allocation will succeed. It will sleep
+ * for as long as it takes to complete the allocation.
+ *
+ * If there was insufficient memory to complete the request, return NULL.
+ */
+static inline void *ntfs_malloc_nofs_nofail(unsigned long size)
+{
+ return __ntfs_malloc(size, GFP_NOFS | __GFP_HIGHMEM | __GFP_NOFAIL);
+}
+
static inline void ntfs_free(void *addr)
{
if (likely(((unsigned long)addr < VMALLOC_START) ||
[PATCH 3/25] NTFS: Use ntfs_malloc_nofs_nofail() in ntfs_runlists_merge()
in the two critical regions. This means we no longer need to
panic() when the allocation fails as it now cannot fail.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 3 ++
fs/ntfs/runlist.c | 68 +++++++++++++++++++++++++++++++++++++++--------------
2 files changed, 53 insertions(+), 18 deletions(-)
9529d461d0992959026264b8fc002ac01d226708
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -40,6 +40,9 @@ ToDo/Notes:
- Add fs/ntfs/malloc.h::ntfs_malloc_nofs_nofail() which is analogous to
ntfs_malloc_nofs() but it performs allocations with __GFP_NOFAIL and
hence cannot fail.
+ - Use ntfs_malloc_nofs_nofail() in the two critical regions in
+ fs/ntfs/runlist.c::ntfs_runlists_merge(). This means we no longer
+ need to panic() if the allocation fails as it now cannot fail.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
--- a/fs/ntfs/runlist.c
+++ b/fs/ntfs/runlist.c
@@ -35,7 +35,7 @@ static inline void ntfs_rl_mm(runlist_el
int size)
{
if (likely((dst != src) && (size > 0)))
- memmove(base + dst, base + src, size * sizeof (*base));
+ memmove(base + dst, base + src, size * sizeof(*base));
}
/**
@@ -95,6 +95,51 @@ static inline runlist_element *ntfs_rl_r
}
/**
+ * ntfs_rl_realloc_nofail - Reallocate memory for runlists
+ * @rl: original runlist
+ * @old_size: number of runlist elements in the original runlist @rl
+ * @new_size: number of runlist elements we need space for
+ *
+ * As the runlists grow, more memory will be required. To prevent the
+ * kernel having to allocate and reallocate large numbers of small bits of
+ * memory, this function returns an entire page of memory.
+ *
+ * This function guarantees that the allocation will succeed. It will sleep
+ * for as long as it takes to complete the allocation.
+ *
+ * It is up to the caller to serialize access to the runlist @rl.
+ *
+ * N.B. If the new allocation doesn't require a different number of pages in
+ * memory, the function will return the original pointer.
+ *
+ * On success, return a pointer to the newly allocated, or recycled, memory.
+ * On error, return -errno. The following error codes are defined:
+ * -ENOMEM - Not enough memory to allocate runlist array.
+ * -EINVAL - Invalid parameters were passed in.
+ */
+static inline runlist_element *ntfs_rl_realloc_nofail(runlist_element *rl,
+ int old_size, int new_size)
+{
+ runlist_element *new_rl;
+
+ old_size = PAGE_ALIGN(old_size * sizeof(*rl));
+ new_size = PAGE_ALIGN(new_size * sizeof(*rl));
+ if (old_size == new_size)
+ return rl;
+
+ new_rl = ntfs_malloc_nofs_nofail(new_size);
+ BUG_ON(!new_rl);
+
+ if (likely(rl != NULL)) {
+ if (unlikely(old_size > new_size))
+ old_size = new_size;
+ memcpy(new_rl, rl, old_size);
+ ntfs_free(rl);
+ }
+ return new_rl;
+}
+
+/**
* ntfs_are_rl_mergeable - test if two runlists can be joined together
* @dst: original runlist
* @src: new runlist to test for mergeability with @dst
@@ -621,11 +666,8 @@ runlist_element *ntfs_runlists_merge(run
if (drl[ds].lcn != LCN_RL_NOT_MAPPED) {
/* Add an unmapped runlist element. */
if (!slots) {
- /* FIXME/TODO: We need to have the
- * extra memory already! (AIA) */
- drl = ntfs_rl_realloc(drl, ds, ds + 2);
- if (!drl)
- goto critical_error;
+ drl = ntfs_rl_realloc_nofail(drl, ds,
+ ds + 2);
slots = 2;
}
ds++;
@@ -640,13 +682,8 @@ runlist_element *ntfs_runlists_merge(run
drl[ds].length = marker_vcn - drl[ds].vcn;
/* Finally add the ENOENT terminator. */
ds++;
- if (!slots) {
- /* FIXME/TODO: We need to have the extra
- * memory already! (AIA) */
- drl = ntfs_rl_realloc(drl, ds, ds + 1);
- if (!drl)
- goto critical_error;
- }
+ if (!slots)
+ drl = ntfs_rl_realloc_nofail(drl, ds, ds + 1);
drl[ds].vcn = marker_vcn;
drl[ds].lcn = LCN_ENOENT;
drl[ds].length = (s64)0;
@@ -659,11 +696,6 @@ finished:
ntfs_debug("Merged runlist:");
ntfs_debug_dump_runlist(drl);
return drl;
-
-critical_error:
- /* Critical error! We cannot afford to fail here. */
- ntfs_error(NULL, "Critical error! Not enough memory.");
- panic("NTFS: Cannot continue.");
}
/**
[PATCH 4/25] NTFS: Fix two nasty runlist merging bugs that had gone unnoticed
so far. Thanks to Stefano Picerno for the bug report.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2 ++
fs/ntfs/runlist.c | 5 +++--
2 files changed, 5 insertions(+), 2 deletions(-)
84d6ebe63f50b6efd8be252b58a207132157c60f
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -43,6 +43,8 @@ ToDo/Notes:
- Use ntfs_malloc_nofs_nofail() in the two critical regions in
fs/ntfs/runlist.c::ntfs_runlists_merge(). This means we no longer
need to panic() if the allocation fails as it now cannot fail.
+ - Fix two nasty runlist merging bugs that had gone unnoticed so far.
+ Thanks to Stefano Picerno for the bug report.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
--- a/fs/ntfs/runlist.c
+++ b/fs/ntfs/runlist.c
@@ -542,6 +542,7 @@ runlist_element *ntfs_runlists_merge(run
/* Scan to the end of the source runlist. */
for (dend = 0; likely(drl[dend].length); dend++)
;
+ dend++;
drl = ntfs_rl_realloc(drl, dend, dend + 1);
if (IS_ERR(drl))
return drl;
@@ -611,8 +612,8 @@ runlist_element *ntfs_runlists_merge(run
((drl[dins].vcn + drl[dins].length) <= /* End of hole */
(srl[send - 1].vcn + srl[send - 1].length)));
- /* Or we'll lose an end marker */
- if (start && finish && (drl[dins].length == 0))
+ /* Or we will lose an end marker. */
+ if (finish && !drl[dins].length)
ss++;
if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn))
finish = FALSE;
[PATCH 5/25] NTFS: Remove two bogus BUG_ON()s from fs/ntfs/mft.c.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 1 +
fs/ntfs/mft.c | 2 --
2 files changed, 1 insertions(+), 2 deletions(-)
8bb735216a0675e247bbe8b8b92c09d6884d1a17
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -45,6 +45,7 @@ ToDo/Notes:
need to panic() if the allocation fails as it now cannot fail.
- Fix two nasty runlist merging bugs that had gone unnoticed so far.
Thanks to Stefano Picerno for the bug report.
+ - Remove two bogus BUG_ON()s from fs/ntfs/mft.c.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
--- a/fs/ntfs/mft.c
+++ b/fs/ntfs/mft.c
@@ -511,7 +511,6 @@ int ntfs_sync_mft_mirror(ntfs_volume *vo
} while (bh);
tail->b_this_page = head;
attach_page_buffers(page, head);
- BUG_ON(!page_has_buffers(page));
}
bh = head = page_buffers(page);
BUG_ON(!bh);
@@ -692,7 +691,6 @@ int write_mft_record_nolock(ntfs_inode *
*/
if (!NInoTestClearDirty(ni))
goto done;
- BUG_ON(!page_has_buffers(page));
bh = head = page_buffers(page);
BUG_ON(!bh);
rl = NULL;
[PATCH 6/25] NTFS: Fix handling of valid but empty mapping pairs array in
fs/ntfs/runlist.c::ntfs_mapping_pairs_decompress().
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2 ++
fs/ntfs/runlist.c | 3 +++
2 files changed, 5 insertions(+), 0 deletions(-)
2b0ada2b8e086c267dd116a39ad41ff0a717b665
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -46,6 +46,8 @@ ToDo/Notes:
- Fix two nasty runlist merging bugs that had gone unnoticed so far.
Thanks to Stefano Picerno for the bug report.
- Remove two bogus BUG_ON()s from fs/ntfs/mft.c.
+ - Fix handling of valid but empty mapping pairs array in
+ fs/ntfs/runlist.c::ntfs_mapping_pairs_decompress().
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
--- a/fs/ntfs/runlist.c
+++ b/fs/ntfs/runlist.c
@@ -760,6 +760,9 @@ runlist_element *ntfs_mapping_pairs_deco
ntfs_error(vol->sb, "Corrupt attribute.");
return ERR_PTR(-EIO);
}
+ /* If the mapping pairs array is valid but empty, nothing to do. */
+ if (!vcn && !*buf)
+ return old_rl;
/* Current position in runlist array. */
rlpos = 0;
/* Allocate first page and set current runlist size to one page. */
[PATCH 7/25] NTFS: Report unrepresentable inodes during ntfs_readdir() as KERN_WARNING
messages and include the inode number. Thanks to Yura Pakhuchiy for
pointing this out.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 3 +++
fs/ntfs/dir.c | 3 ++-
fs/ntfs/unistr.c | 3 ++-
3 files changed, 7 insertions(+), 2 deletions(-)
f94ad38e68e1623660fdbb063d0c580ba6661c29
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -48,6 +48,9 @@ ToDo/Notes:
- Remove two bogus BUG_ON()s from fs/ntfs/mft.c.
- Fix handling of valid but empty mapping pairs array in
fs/ntfs/runlist.c::ntfs_mapping_pairs_decompress().
+ - Report unrepresentable inodes during ntfs_readdir() as KERN_WARNING
+ messages and include the inode number. Thanks to Yura Pakhuchiy for
+ pointing this out.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c
--- a/fs/ntfs/dir.c
+++ b/fs/ntfs/dir.c
@@ -1051,7 +1051,8 @@ static inline int ntfs_filldir(ntfs_volu
ie->key.file_name.file_name_length, &name,
NTFS_MAX_NAME_LEN * NLS_MAX_CHARSET_SIZE + 1);
if (name_len <= 0) {
- ntfs_debug("Skipping unrepresentable file.");
+ ntfs_warning(vol->sb, "Skipping unrepresentable inode 0x%llx.",
+ (long long)MREF_LE(ie->data.dir.indexed_file));
return 0;
}
if (ie->key.file_name.file_attributes &
diff --git a/fs/ntfs/unistr.c b/fs/ntfs/unistr.c
--- a/fs/ntfs/unistr.c
+++ b/fs/ntfs/unistr.c
@@ -372,7 +372,8 @@ retry: wc = nls->uni2char(le16_to_cpu(
return -EINVAL;
conversion_err:
ntfs_error(vol->sb, "Unicode name contains characters that cannot be "
- "converted to character set %s.", nls->charset);
+ "converted to character set %s. You might want to "
+ "try to use the mount option nls=utf8.", nls->charset);
if (ns != *outs)
kfree(ns);
if (wc != -ENAMETOOLONG)
[PATCH 8/25] NTFS: Change ntfs_rl_truncate_nolock() to throw away the runlist if the new
length is zero.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2 ++
fs/ntfs/runlist.c | 14 +++++++++++++-
2 files changed, 15 insertions(+), 1 deletions(-)
3ffc5a443824fcf426d8d35dc632acc4dd9fb6d1
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -51,6 +51,8 @@ ToDo/Notes:
- Report unrepresentable inodes during ntfs_readdir() as KERN_WARNING
messages and include the inode number. Thanks to Yura Pakhuchiy for
pointing this out.
+ - Change ntfs_rl_truncate_nolock() to throw away the runlist if the new
+ length is zero.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
--- a/fs/ntfs/runlist.c
+++ b/fs/ntfs/runlist.c
@@ -1455,6 +1455,7 @@ err_out:
/**
* ntfs_rl_truncate_nolock - truncate a runlist starting at a specified vcn
+ * @vol: ntfs volume (needed for error output)
* @runlist: runlist to truncate
* @new_length: the new length of the runlist in VCNs
*
@@ -1462,12 +1463,16 @@ err_out:
* holding the runlist elements to a length of @new_length VCNs.
*
* If @new_length lies within the runlist, the runlist elements with VCNs of
- * @new_length and above are discarded.
+ * @new_length and above are discarded. As a special case if @new_length is
+ * zero, the runlist is discarded and set to NULL.
*
* If @new_length lies beyond the runlist, a sparse runlist element is added to
* the end of the runlist @runlist or if the last runlist element is a sparse
* one already, this is extended.
*
+ * Note, no checking is done for unmapped runlist elements. It is assumed that
+ * the caller has mapped any elements that need to be mapped already.
+ *
* Return 0 on success and -errno on error.
*
* Locking: The caller must hold @runlist->lock for writing.
@@ -1482,6 +1487,13 @@ int ntfs_rl_truncate_nolock(const ntfs_v
BUG_ON(!runlist);
BUG_ON(new_length < 0);
rl = runlist->rl;
+ if (!new_length) {
+ ntfs_debug("Freeing runlist.");
+ runlist->rl = NULL;
+ if (rl)
+ ntfs_free(rl);
+ return 0;
+ }
if (unlikely(!rl)) {
/*
* Create a runlist consisting of a sparse runlist element of
[PATCH 9/25] NTFS: Add ntfs_rl_punch_nolock() which punches a caller specified hole into a runlist.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2
fs/ntfs/runlist.c | 284 +++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/ntfs/runlist.h | 3 +
3 files changed, 289 insertions(+), 0 deletions(-)
6e48321a40610f7213e3ac75ba234f6f8b3ed5f5
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -53,6 +53,8 @@ ToDo/Notes:
pointing this out.
- Change ntfs_rl_truncate_nolock() to throw away the runlist if the new
length is zero.
+ - Add runlist.[hc]::ntfs_rl_punch_nolock() which punches a caller
+ specified hole into a runlist.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
--- a/fs/ntfs/runlist.c
+++ b/fs/ntfs/runlist.c
@@ -1601,4 +1601,288 @@ int ntfs_rl_truncate_nolock(const ntfs_v
return 0;
}
+/**
+ * ntfs_rl_punch_nolock - punch a hole into a runlist
+ * @vol: ntfs volume (needed for error output)
+ * @runlist: runlist to punch a hole into
+ * @start: starting VCN of the hole to be created
+ * @length: size of the hole to be created in units of clusters
+ *
+ * Punch a hole into the runlist @runlist starting at VCN @start and of size
+ * @length clusters.
+ *
+ * Return 0 on success and -errno on error, in which case @runlist has not been
+ * modified.
+ *
+ * If @start and/or @start + @length are outside the runlist return error code
+ * -ENOENT.
+ *
+ * If the runlist contains unmapped or error elements between @start and @start
+ * + @length return error code -EINVAL.
+ *
+ * Locking: The caller must hold @runlist->lock for writing.
+ */
+int ntfs_rl_punch_nolock(const ntfs_volume *vol, runlist *const runlist,
+ const VCN start, const s64 length)
+{
+ const VCN end = start + length;
+ s64 delta;
+ runlist_element *rl, *rl_end, *rl_real_end, *trl;
+ int old_size;
+ BOOL lcn_fixup = FALSE;
+
+ ntfs_debug("Entering for start 0x%llx, length 0x%llx.",
+ (long long)start, (long long)length);
+ BUG_ON(!runlist);
+ BUG_ON(start < 0);
+ BUG_ON(length < 0);
+ BUG_ON(end < 0);
+ rl = runlist->rl;
+ if (unlikely(!rl)) {
+ if (likely(!start && !length))
+ return 0;
+ return -ENOENT;
+ }
+ /* Find @start in the runlist. */
+ while (likely(rl->length && start >= rl[1].vcn))
+ rl++;
+ rl_end = rl;
+ /* Find @end in the runlist. */
+ while (likely(rl_end->length && end >= rl_end[1].vcn)) {
+ /* Verify there are no unmapped or error elements. */
+ if (unlikely(rl_end->lcn < LCN_HOLE))
+ return -EINVAL;
+ rl_end++;
+ }
+ /* Check the last element. */
+ if (unlikely(rl_end->length && rl_end->lcn < LCN_HOLE))
+ return -EINVAL;
+ /* This covers @start being out of bounds, too. */
+ if (!rl_end->length && end > rl_end->vcn)
+ return -ENOENT;
+ if (!length)
+ return 0;
+ if (!rl->length)
+ return -ENOENT;
+ rl_real_end = rl_end;
+ /* Determine the runlist size. */
+ while (likely(rl_real_end->length))
+ rl_real_end++;
+ old_size = rl_real_end - runlist->rl + 1;
+ /* If @start is in a hole simply extend the hole. */
+ if (rl->lcn == LCN_HOLE) {
+ /*
+ * If both @start and @end are in the same sparse run, we are
+ * done.
+ */
+ if (end <= rl[1].vcn) {
+ ntfs_debug("Done (requested hole is already sparse).");
+ return 0;
+ }
+extend_hole:
+ /* Extend the hole. */
+ rl->length = end - rl->vcn;
+ /* If @end is in a hole, merge it with the current one. */
+ if (rl_end->lcn == LCN_HOLE) {
+ rl_end++;
+ rl->length = rl_end->vcn - rl->vcn;
+ }
+ /* We have done the hole. Now deal with the remaining tail. */
+ rl++;
+ /* Cut out all runlist elements up to @end. */
+ if (rl < rl_end)
+ memmove(rl, rl_end, (rl_real_end - rl_end + 1) *
+ sizeof(*rl));
+ /* Adjust the beginning of the tail if necessary. */
+ if (end > rl->vcn) {
+ s64 delta = end - rl->vcn;
+ rl->vcn = end;
+ rl->length -= delta;
+ /* Only adjust the lcn if it is real. */
+ if (rl->lcn >= 0)
+ rl->lcn += delta;
+ }
+shrink_allocation:
+ /* Reallocate memory if the allocation changed. */
+ if (rl < rl_end) {
+ rl = ntfs_rl_realloc(runlist->rl, old_size,
+ old_size - (rl_end - rl));
+ if (IS_ERR(rl))
+ ntfs_warning(vol->sb, "Failed to shrink "
+ "runlist buffer. This just "
+ "wastes a bit of memory "
+ "temporarily so we ignore it "
+ "and return success.");
+ else
+ runlist->rl = rl;
+ }
+ ntfs_debug("Done (extend hole).");
+ return 0;
+ }
+ /*
+ * If @start is at the beginning of a run things are easier as there is
+ * no need to split the first run.
+ */
+ if (start == rl->vcn) {
+ /*
+ * @start is at the beginning of a run.
+ *
+ * If the previous run is sparse, extend its hole.
+ *
+ * If @end is not in the same run, switch the run to be sparse
+ * and extend the newly created hole.
+ *
+ * Thus both of these cases reduce the problem to the above
+ * case of "@start is in a hole".
+ */
+ if (rl > runlist->rl && (rl - 1)->lcn == LCN_HOLE) {
+ rl--;
+ goto extend_hole;
+ }
+ if (end >= rl[1].vcn) {
+ rl->lcn = LCN_HOLE;
+ goto extend_hole;
+ }
+ /*
+ * The final case is when @end is in the same run as @start.
+ * For this need to split the run into two. One run for the
+ * sparse region between the beginning of the old run, i.e.
+ * @start, and @end and one for the remaining non-sparse
+ * region, i.e. between @end and the end of the old run.
+ */
+ trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 1);
+ if (IS_ERR(trl))
+ goto enomem_out;
+ old_size++;
+ if (runlist->rl != trl) {
+ rl = trl + (rl - runlist->rl);
+ rl_end = trl + (rl_end - runlist->rl);
+ rl_real_end = trl + (rl_real_end - runlist->rl);
+ runlist->rl = trl;
+ }
+split_end:
+ /* Shift all the runs up by one. */
+ memmove(rl + 1, rl, (rl_real_end - rl + 1) * sizeof(*rl));
+ /* Finally, setup the two split runs. */
+ rl->lcn = LCN_HOLE;
+ rl->length = length;
+ rl++;
+ rl->vcn += length;
+ /* Only adjust the lcn if it is real. */
+ if (rl->lcn >= 0 || lcn_fixup)
+ rl->lcn += length;
+ rl->length -= length;
+ ntfs_debug("Done (split one).");
+ return 0;
+ }
+ /*
+ * @start is neither in a hole nor at the beginning of a run.
+ *
+ * If @end is in a hole, things are easier as simply truncating the run
+ * @start is in to end at @start - 1, deleting all runs after that up
+ * to @end, and finally extending the beginning of the run @end is in
+ * to be @start is all that is needed.
+ */
+ if (rl_end->lcn == LCN_HOLE) {
+ /* Truncate the run containing @start. */
+ rl->length = start - rl->vcn;
+ rl++;
+ /* Cut out all runlist elements up to @end. */
+ if (rl < rl_end)
+ memmove(rl, rl_end, (rl_real_end - rl_end + 1) *
+ sizeof(*rl));
+ /* Extend the beginning of the run @end is in to be @start. */
+ rl->vcn = start;
+ rl->length = rl[1].vcn - start;
+ goto shrink_allocation;
+ }
+ /*
+ * If @end is not in a hole there are still two cases to distinguish.
+ * Either @end is or is not in the same run as @start.
+ *
+ * The second case is easier as it can be reduced to an already solved
+ * problem by truncating the run @start is in to end at @start - 1.
+ * Then, if @end is in the next run need to split the run into a sparse
+ * run followed by a non-sparse run (already covered above) and if @end
+ * is not in the next run switching it to be sparse, again reduces the
+ * problem to the already covered case of "@start is in a hole".
+ */
+ if (end >= rl[1].vcn) {
+ /*
+ * If @end is not in the next run, reduce the problem to the
+ * case of "@start is in a hole".
+ */
+ if (rl[1].length && end >= rl[2].vcn) {
+ /* Truncate the run containing @start. */
+ rl->length = start - rl->vcn;
+ rl++;
+ rl->vcn = start;
+ rl->lcn = LCN_HOLE;
+ goto extend_hole;
+ }
+ trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 1);
+ if (IS_ERR(trl))
+ goto enomem_out;
+ old_size++;
+ if (runlist->rl != trl) {
+ rl = trl + (rl - runlist->rl);
+ rl_end = trl + (rl_end - runlist->rl);
+ rl_real_end = trl + (rl_real_end - runlist->rl);
+ runlist->rl = trl;
+ }
+ /* Truncate the run containing @start. */
+ rl->length = start - rl->vcn;
+ rl++;
+ /*
+ * @end is in the next run, reduce the problem to the case
+ * where "@start is at the beginning of a run and @end is in
+ * the same run as @start".
+ */
+ delta = rl->vcn - start;
+ rl->vcn = start;
+ if (rl->lcn >= 0) {
+ rl->lcn -= delta;
+ /* Need this in case the lcn just became negative. */
+ lcn_fixup = TRUE;
+ }
+ rl->length += delta;
+ goto split_end;
+ }
+ /*
+ * The first case from above, i.e. @end is in the same run as @start.
+ * We need to split the run into three. One run for the non-sparse
+ * region between the beginning of the old run and @start, one for the
+ * sparse region between @start and @end, and one for the remaining
+ * non-sparse region, i.e. between @end and the end of the old run.
+ */
+ trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 2);
+ if (IS_ERR(trl))
+ goto enomem_out;
+ old_size += 2;
+ if (runlist->rl != trl) {
+ rl = trl + (rl - runlist->rl);
+ rl_end = trl + (rl_end - runlist->rl);
+ rl_real_end = trl + (rl_real_end - runlist->rl);
+ runlist->rl = trl;
+ }
+ /* Shift all the runs up by two. */
+ memmove(rl + 2, rl, (rl_real_end - rl + 1) * sizeof(*rl));
+ /* Finally, setup the three split runs. */
+ rl->length = start - rl->vcn;
+ rl++;
+ rl->vcn = start;
+ rl->lcn = LCN_HOLE;
+ rl->length = length;
+ rl++;
+ delta = end - rl->vcn;
+ rl->vcn = end;
+ rl->lcn += delta;
+ rl->length -= delta;
+ ntfs_debug("Done (split both).");
+ return 0;
+enomem_out:
+ ntfs_error(vol->sb, "Not enough memory to extend runlist buffer.");
+ return -ENOMEM;
+}
+
#endif /* NTFS_RW */
diff --git a/fs/ntfs/runlist.h b/fs/ntfs/runlist.h
--- a/fs/ntfs/runlist.h
+++ b/fs/ntfs/runlist.h
@@ -94,6 +94,9 @@ extern int ntfs_mapping_pairs_build(cons
extern int ntfs_rl_truncate_nolock(const ntfs_volume *vol,
runlist *const runlist, const s64 new_length);
+int ntfs_rl_punch_nolock(const ntfs_volume *vol, runlist *const runlist,
+ const VCN start, const s64 length);
+
#endif /* NTFS_RW */
#endif /* _LINUX_NTFS_RUNLIST_H */
[PATCH 10/25] NTFS: Fix a bug in fs/ntfs/index.c::ntfs_index_lookup(). When the returned
index entry is in the index root, we forgot to set the @ir pointer in
the index context. Thanks for Yura Pakhuchiy for finding this bug.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 3 +++
fs/ntfs/index.c | 1 +
2 files changed, 4 insertions(+), 0 deletions(-)
8e08ceaeacd5d300aaad166f2eef8bfc37e09831
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -55,6 +55,9 @@ ToDo/Notes:
length is zero.
- Add runlist.[hc]::ntfs_rl_punch_nolock() which punches a caller
specified hole into a runlist.
+ - Fix a bug in fs/ntfs/index.c::ntfs_index_lookup(). When the returned
+ index entry is in the index root, we forgot to set the @ir pointer in
+ the index context. Thanks to Yura Pakhuchiy for finding this bug.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c
--- a/fs/ntfs/index.c
+++ b/fs/ntfs/index.c
@@ -205,6 +205,7 @@ int ntfs_index_lookup(const void *key, c
&ie->key, key_len)) {
ir_done:
ictx->is_in_root = TRUE;
+ ictx->ir = ir;
ictx->actx = actx;
ictx->base_ni = base_ni;
ictx->ia = NULL;
[PATCH 11/25] NTFS: Remove bogus setting of PageError in ntfs_read_compressed_block().
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 1 +
fs/ntfs/compress.c | 8 --------
fs/ntfs/file.c | 9 +++++++--
3 files changed, 8 insertions(+), 10 deletions(-)
f25dfb5e44fa8641961780d681bc1871abcfb861
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -58,6 +58,7 @@ ToDo/Notes:
- Fix a bug in fs/ntfs/index.c::ntfs_index_lookup(). When the returned
index entry is in the index root, we forgot to set the @ir pointer in
the index context. Thanks to Yura Pakhuchiy for finding this bug.
+ - Remove bogus setting of PageError in ntfs_read_compressed_block().
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c
--- a/fs/ntfs/compress.c
+++ b/fs/ntfs/compress.c
@@ -539,7 +539,6 @@ int ntfs_read_compressed_block(struct pa
if (unlikely(!pages || !bhs)) {
kfree(bhs);
kfree(pages);
- SetPageError(page);
unlock_page(page);
ntfs_error(vol->sb, "Failed to allocate internal buffers.");
return -ENOMEM;
@@ -871,9 +870,6 @@ lock_retry_remap:
for (; prev_cur_page < cur_page; prev_cur_page++) {
page = pages[prev_cur_page];
if (page) {
- if (prev_cur_page == xpage &&
- !xpage_done)
- SetPageError(page);
flush_dcache_page(page);
kunmap(page);
unlock_page(page);
@@ -904,8 +900,6 @@ lock_retry_remap:
"Terminating them with extreme "
"prejudice. Inode 0x%lx, page index "
"0x%lx.", ni->mft_no, page->index);
- if (cur_page == xpage && !xpage_done)
- SetPageError(page);
flush_dcache_page(page);
kunmap(page);
unlock_page(page);
@@ -953,8 +947,6 @@ err_out:
for (i = cur_page; i < max_page; i++) {
page = pages[i];
if (page) {
- if (i == xpage && !xpage_done)
- SetPageError(page);
flush_dcache_page(page);
kunmap(page);
unlock_page(page);
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -1,7 +1,7 @@
/*
- * file.c - NTFS kernel file operations. Part of the Linux-NTFS project.
+ * file.c - NTFS kernel file operations. Part of the Linux-NTFS project.
*
- * Copyright (c) 2001-2004 Anton Altaparmakov
+ * Copyright (c) 2001-2005 Anton Altaparmakov
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
@@ -94,6 +94,11 @@ static int ntfs_file_fsync(struct file *
if (!datasync || !NInoNonResident(NTFS_I(vi)))
ret = ntfs_write_inode(vi, 1);
write_inode_now(vi, !datasync);
+ /*
+ * NOTE: If we were to use mapping->private_list (see ext2 and
+ * fs/buffer.c) for dirty blocks then we could optimize the below to be
+ * sync_mapping_buffers(vi->i_mapping).
+ */
err = sync_blockdev(vi->i_sb->s_bdev);
if (unlikely(err && !ret))
ret = err;
[PATCH 12/25] NTFS: Add fs/ntfs/attrib.[hc]::ntfs_resident_attr_value_resize().
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 1 +
fs/ntfs/attrib.c | 40 ++++++++++++++++++++++++++++++++++++++++
fs/ntfs/attrib.h | 2 ++
3 files changed, 43 insertions(+), 0 deletions(-)
0aacceacf35451ffb771ec825555e98c5dce8b01
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -59,6 +59,7 @@ ToDo/Notes:
index entry is in the index root, we forgot to set the @ir pointer in
the index context. Thanks to Yura Pakhuchiy for finding this bug.
- Remove bogus setting of PageError in ntfs_read_compressed_block().
+ - Add fs/ntfs/attrib.[hc]::ntfs_resident_attr_value_resize().
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -1247,6 +1247,46 @@ int ntfs_attr_record_resize(MFT_RECORD *
}
/**
+ * ntfs_resident_attr_value_resize - resize the value of a resident attribute
+ * @m: mft record containing attribute record
+ * @a: attribute record whose value to resize
+ * @new_size: new size in bytes to which to resize the attribute value of @a
+ *
+ * Resize the value of the attribute @a in the mft record @m to @new_size bytes.
+ * If the value is made bigger, the newly allocated space is cleared.
+ *
+ * Return 0 on success and -errno on error. The following error codes are
+ * defined:
+ * -ENOSPC - Not enough space in the mft record @m to perform the resize.
+ *
+ * Note: On error, no modifications have been performed whatsoever.
+ *
+ * Warning: If you make a record smaller without having copied all the data you
+ * are interested in the data may be overwritten.
+ */
+int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
+ const u32 new_size)
+{
+ u32 old_size;
+
+ /* Resize the resident part of the attribute record. */
+ if (ntfs_attr_record_resize(m, a,
+ le16_to_cpu(a->data.resident.value_offset) + new_size))
+ return -ENOSPC;
+ /*
+ * The resize succeeded! If we made the attribute value bigger, clear
+ * the area between the old size and @new_size.
+ */
+ old_size = le32_to_cpu(a->data.resident.value_length);
+ if (new_size > old_size)
+ memset((u8*)a + le16_to_cpu(a->data.resident.value_offset) +
+ old_size, 0, new_size - old_size);
+ /* Finally update the length of the attribute value. */
+ a->data.resident.value_length = cpu_to_le32(new_size);
+ return 0;
+}
+
+/**
* ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
* @ni: ntfs inode describing the attribute to convert
*
diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h
--- a/fs/ntfs/attrib.h
+++ b/fs/ntfs/attrib.h
@@ -99,6 +99,8 @@ extern int ntfs_attr_can_be_resident(con
const ATTR_TYPE type);
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
+extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
+ const u32 new_size);
extern int ntfs_attr_make_non_resident(ntfs_inode *ni);
[PATCH 13/25] NTFS: Fix several bugs in fs/ntfs/attrib.c.
- Fix a bug in ntfs_map_runlist_nolock() where we forgot to protect
access to the allocated size in the ntfs inode with the size lock.
- Fix ntfs_attr_vcn_to_lcn_nolock() and ntfs_attr_find_vcn_nolock() to
return LCN_ENOENT when there is no runlist and the allocated size is
zero.
- Fix load_attribute_list() to handle the case of a NULL runlist.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 6 ++++++
fs/ntfs/attrib.c | 32 +++++++++++++++++++++++++++++++-
2 files changed, 37 insertions(+), 1 deletions(-)
2983d1bd1a596e88cdddc0c2d45b9e97728f3f41
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -60,6 +60,12 @@ ToDo/Notes:
the index context. Thanks to Yura Pakhuchiy for finding this bug.
- Remove bogus setting of PageError in ntfs_read_compressed_block().
- Add fs/ntfs/attrib.[hc]::ntfs_resident_attr_value_resize().
+ - Fix a bug in ntfs_map_runlist_nolock() where we forgot to protect
+ access to the allocated size in the ntfs inode with the size lock.
+ - Fix ntfs_attr_vcn_to_lcn_nolock() and ntfs_attr_find_vcn_nolock() to
+ return LCN_ENOENT when there is no runlist and the allocated size is
+ zero.
+ - Fix load_attribute_list() to handle the case of a NULL runlist.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -43,6 +43,9 @@
* which is not an error as such. This is -ENOENT. It means that @vcn is out
* of bounds of the runlist.
*
+ * Note the runlist can be NULL after this function returns if @vcn is zero and
+ * the attribute has zero allocated size, i.e. there simply is no runlist.
+ *
* Locking: - The runlist must be locked for writing.
* - This function modifies the runlist.
*/
@@ -54,6 +57,7 @@ int ntfs_map_runlist_nolock(ntfs_inode *
ATTR_RECORD *a;
ntfs_attr_search_ctx *ctx;
runlist_element *rl;
+ unsigned long flags;
int err = 0;
ntfs_debug("Mapping runlist part containing vcn 0x%llx.",
@@ -85,8 +89,11 @@ int ntfs_map_runlist_nolock(ntfs_inode *
* ntfs_mapping_pairs_decompress() fails.
*/
end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1;
- if (unlikely(!a->data.non_resident.lowest_vcn && end_vcn <= 1))
+ if (unlikely(!a->data.non_resident.lowest_vcn && end_vcn <= 1)) {
+ read_lock_irqsave(&ni->size_lock, flags);
end_vcn = ni->allocated_size >> ni->vol->cluster_size_bits;
+ read_unlock_irqrestore(&ni->size_lock, flags);
+ }
if (unlikely(vcn >= end_vcn)) {
err = -ENOENT;
goto err_out;
@@ -165,6 +172,7 @@ LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_ino
const BOOL write_locked)
{
LCN lcn;
+ unsigned long flags;
BOOL is_retry = FALSE;
ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.",
@@ -173,6 +181,14 @@ LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_ino
BUG_ON(!ni);
BUG_ON(!NInoNonResident(ni));
BUG_ON(vcn < 0);
+ if (!ni->runlist.rl) {
+ read_lock_irqsave(&ni->size_lock, flags);
+ if (!ni->allocated_size) {
+ read_unlock_irqrestore(&ni->size_lock, flags);
+ return LCN_ENOENT;
+ }
+ read_unlock_irqrestore(&ni->size_lock, flags);
+ }
retry_remap:
/* Convert vcn to lcn. If that fails map the runlist and retry once. */
lcn = ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn);
@@ -255,6 +271,7 @@ retry_remap:
runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn,
const BOOL write_locked)
{
+ unsigned long flags;
runlist_element *rl;
int err = 0;
BOOL is_retry = FALSE;
@@ -265,6 +282,14 @@ runlist_element *ntfs_attr_find_vcn_nolo
BUG_ON(!ni);
BUG_ON(!NInoNonResident(ni));
BUG_ON(vcn < 0);
+ if (!ni->runlist.rl) {
+ read_lock_irqsave(&ni->size_lock, flags);
+ if (!ni->allocated_size) {
+ read_unlock_irqrestore(&ni->size_lock, flags);
+ return ERR_PTR(-ENOENT);
+ }
+ read_unlock_irqrestore(&ni->size_lock, flags);
+ }
retry_remap:
rl = ni->runlist.rl;
if (likely(rl && vcn >= rl[0].vcn)) {
@@ -528,6 +553,11 @@ int load_attribute_list(ntfs_volume *vol
block_size_bits = sb->s_blocksize_bits;
down_read(&runlist->lock);
rl = runlist->rl;
+ if (!rl) {
+ ntfs_error(sb, "Cannot read attribute list since runlist is "
+ "missing.");
+ goto err_out;
+ }
/* Read all clusters specified by the runlist one run at a time. */
while (rl->length) {
lcn = ntfs_rl_vcn_to_lcn(rl, rl->vcn);
[PATCH 14/25] NTFS: Fix handling of sparse attributes in ntfs_attr_make_non_resident().
Also, add BUG() checks to ntfs_attr_make_non_resident() and
ntfs_attr_set() to ensure that these functions are never called
for compressed or encrypted attributes.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 4 ++++
fs/ntfs/attrib.c | 53 ++++++++++++++++++++++++++++++++++++-----------------
2 files changed, 40 insertions(+), 17 deletions(-)
807c453de7c5487d2e5eece76bafdea8f39d249e
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -66,6 +66,10 @@ ToDo/Notes:
return LCN_ENOENT when there is no runlist and the allocated size is
zero.
- Fix load_attribute_list() to handle the case of a NULL runlist.
+ - Fix handling of sparse attributes in ntfs_attr_make_non_resident().
+ - Add BUG() checks to ntfs_attr_make_non_resident() and ntfs_attr_set()
+ to ensure that these functions are never called for compressed or
+ encrypted attributes.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -1372,6 +1372,12 @@ int ntfs_attr_make_non_resident(ntfs_ino
return err;
}
/*
+ * FIXME: Compressed and encrypted attributes are not supported when
+ * writing and we should never have gotten here for them.
+ */
+ BUG_ON(NInoCompressed(ni));
+ BUG_ON(NInoEncrypted(ni));
+ /*
* The size needs to be aligned to a cluster boundary for allocation
* purposes.
*/
@@ -1447,10 +1453,15 @@ int ntfs_attr_make_non_resident(ntfs_ino
BUG_ON(a->non_resident);
/*
* Calculate new offsets for the name and the mapping pairs array.
- * We assume the attribute is not compressed or sparse.
*/
- name_ofs = (offsetof(ATTR_REC,
- data.non_resident.compressed_size) + 7) & ~7;
+ if (NInoSparse(ni) || NInoCompressed(ni))
+ name_ofs = (offsetof(ATTR_REC,
+ data.non_resident.compressed_size) +
+ sizeof(a->data.non_resident.compressed_size) +
+ 7) & ~7;
+ else
+ name_ofs = (offsetof(ATTR_REC,
+ data.non_resident.compressed_size) + 7) & ~7;
mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
/*
* Determine the size of the resident part of the now non-resident
@@ -1489,24 +1500,23 @@ int ntfs_attr_make_non_resident(ntfs_ino
memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
a->name_length * sizeof(ntfschar));
a->name_offset = cpu_to_le16(name_ofs);
- /*
- * FIXME: For now just clear all of these as we do not support them
- * when writing.
- */
- a->flags &= cpu_to_le16(0xffff & ~le16_to_cpu(ATTR_IS_SPARSE |
- ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK));
/* Setup the fields specific to non-resident attributes. */
a->data.non_resident.lowest_vcn = 0;
a->data.non_resident.highest_vcn = cpu_to_sle64((new_size - 1) >>
vol->cluster_size_bits);
a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs);
- a->data.non_resident.compression_unit = 0;
memset(&a->data.non_resident.reserved, 0,
sizeof(a->data.non_resident.reserved));
a->data.non_resident.allocated_size = cpu_to_sle64(new_size);
a->data.non_resident.data_size =
a->data.non_resident.initialized_size =
cpu_to_sle64(attr_size);
+ if (NInoSparse(ni) || NInoCompressed(ni)) {
+ a->data.non_resident.compression_unit = 4;
+ a->data.non_resident.compressed_size =
+ a->data.non_resident.allocated_size;
+ } else
+ a->data.non_resident.compression_unit = 0;
/* Generate the mapping pairs array into the attribute record. */
err = ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs,
arec_size - mp_ofs, rl, 0, -1, NULL);
@@ -1516,16 +1526,19 @@ int ntfs_attr_make_non_resident(ntfs_ino
goto undo_err_out;
}
/* Setup the in-memory attribute structure to be non-resident. */
- /*
- * FIXME: For now just clear all of these as we do not support them
- * when writing.
- */
- NInoClearSparse(ni);
- NInoClearEncrypted(ni);
- NInoClearCompressed(ni);
ni->runlist.rl = rl;
write_lock_irqsave(&ni->size_lock, flags);
ni->allocated_size = new_size;
+ if (NInoSparse(ni) || NInoCompressed(ni)) {
+ ni->itype.compressed.size = ni->allocated_size;
+ ni->itype.compressed.block_size = 1U <<
+ (a->data.non_resident.compression_unit +
+ vol->cluster_size_bits);
+ ni->itype.compressed.block_size_bits =
+ ffs(ni->itype.compressed.block_size) - 1;
+ ni->itype.compressed.block_clusters = 1U <<
+ a->data.non_resident.compression_unit;
+ }
write_unlock_irqrestore(&ni->size_lock, flags);
/*
* This needs to be last since the address space operations ->readpage
@@ -1673,6 +1686,12 @@ int ntfs_attr_set(ntfs_inode *ni, const
BUG_ON(cnt < 0);
if (!cnt)
goto done;
+ /*
+ * FIXME: Compressed and encrypted attributes are not supported when
+ * writing and we should never have gotten here for them.
+ */
+ BUG_ON(NInoCompressed(ni));
+ BUG_ON(NInoEncrypted(ni));
mapping = VFS_I(ni)->i_mapping;
/* Work out the starting index and page offset. */
idx = ofs >> PAGE_CACHE_SHIFT;
[PATCH 15/25] NTFS: Fix cluster (de)allocators to work when the runlist is NULL and more
importantly to take a locked runlist rather than them locking it
which leads to lock reversal.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 3 +++
fs/ntfs/lcnalloc.c | 39 ++++++++++++++++-----------------------
fs/ntfs/lcnalloc.h | 21 ++++++++++++---------
fs/ntfs/mft.c | 2 +-
4 files changed, 32 insertions(+), 33 deletions(-)
bbf1813fb8ff9d21171bf22e6d1f0e0393601e86
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -70,6 +70,9 @@ ToDo/Notes:
- Add BUG() checks to ntfs_attr_make_non_resident() and ntfs_attr_set()
to ensure that these functions are never called for compressed or
encrypted attributes.
+ - Fix cluster (de)allocators to work when the runlist is NULL and more
+ importantly to take a locked runlist rather than them locking it
+ which leads to lock reversal.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c
--- a/fs/ntfs/lcnalloc.c
+++ b/fs/ntfs/lcnalloc.c
@@ -54,6 +54,8 @@ int ntfs_cluster_free_from_rl_nolock(ntf
int ret = 0;
ntfs_debug("Entering.");
+ if (!rl)
+ return 0;
for (; rl->length; rl++) {
int err;
@@ -163,17 +165,9 @@ runlist_element *ntfs_cluster_alloc(ntfs
BUG_ON(zone < FIRST_ZONE);
BUG_ON(zone > LAST_ZONE);
- /* Return empty runlist if @count == 0 */
- // FIXME: Do we want to just return NULL instead? (AIA)
- if (!count) {
- rl = ntfs_malloc_nofs(PAGE_SIZE);
- if (!rl)
- return ERR_PTR(-ENOMEM);
- rl[0].vcn = start_vcn;
- rl[0].lcn = LCN_RL_NOT_MAPPED;
- rl[0].length = 0;
- return rl;
- }
+ /* Return NULL if @count is zero. */
+ if (!count)
+ return NULL;
/* Take the lcnbmp lock for writing. */
down_write(&vol->lcnbmp_lock);
/*
@@ -788,7 +782,8 @@ out:
* @vi: vfs inode whose runlist describes the clusters to free
* @start_vcn: vcn in the runlist of @vi at which to start freeing clusters
* @count: number of clusters to free or -1 for all clusters
- * @is_rollback: if TRUE this is a rollback operation
+ * @write_locked: true if the runlist is locked for writing
+ * @is_rollback: true if this is a rollback operation
*
* Free @count clusters starting at the cluster @start_vcn in the runlist
* described by the vfs inode @vi.
@@ -806,17 +801,17 @@ out:
* Return the number of deallocated clusters (not counting sparse ones) on
* success and -errno on error.
*
- * Locking: - The runlist described by @vi must be unlocked on entry and is
- * unlocked on return.
- * - This function takes the runlist lock of @vi for reading and
- * sometimes for writing and sometimes modifies the runlist.
+ * Locking: - The runlist described by @vi must be locked on entry and is
+ * locked on return. Note if the runlist is locked for reading the
+ * lock may be dropped and reacquired. Note the runlist may be
+ * modified when needed runlist fragments need to be mapped.
* - The volume lcn bitmap must be unlocked on entry and is unlocked
* on return.
* - This function takes the volume lcn bitmap lock for writing and
* modifies the bitmap contents.
*/
s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
- const BOOL is_rollback)
+ const BOOL write_locked, const BOOL is_rollback)
{
s64 delta, to_free, total_freed, real_freed;
ntfs_inode *ni;
@@ -848,8 +843,7 @@ s64 __ntfs_cluster_free(struct inode *vi
total_freed = real_freed = 0;
- down_read(&ni->runlist.lock);
- rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, FALSE);
+ rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, write_locked);
if (IS_ERR(rl)) {
if (!is_rollback)
ntfs_error(vol->sb, "Failed to find first runlist "
@@ -903,7 +897,7 @@ s64 __ntfs_cluster_free(struct inode *vi
/* Attempt to map runlist. */
vcn = rl->vcn;
- rl = ntfs_attr_find_vcn_nolock(ni, vcn, FALSE);
+ rl = ntfs_attr_find_vcn_nolock(ni, vcn, write_locked);
if (IS_ERR(rl)) {
err = PTR_ERR(rl);
if (!is_rollback)
@@ -950,7 +944,6 @@ s64 __ntfs_cluster_free(struct inode *vi
/* Update the total done clusters. */
total_freed += to_free;
}
- up_read(&ni->runlist.lock);
if (likely(!is_rollback))
up_write(&vol->lcnbmp_lock);
@@ -960,7 +953,6 @@ s64 __ntfs_cluster_free(struct inode *vi
ntfs_debug("Done.");
return real_freed;
err_out:
- up_read(&ni->runlist.lock);
if (is_rollback)
return err;
/* If no real clusters were freed, no need to rollback. */
@@ -973,7 +965,8 @@ err_out:
* If rollback fails, set the volume errors flag, emit an error
* message, and return the error code.
*/
- delta = __ntfs_cluster_free(vi, start_vcn, total_freed, TRUE);
+ delta = __ntfs_cluster_free(vi, start_vcn, total_freed, write_locked,
+ TRUE);
if (delta < 0) {
ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving "
"inconsistent metadata! Unmount and run "
diff --git a/fs/ntfs/lcnalloc.h b/fs/ntfs/lcnalloc.h
--- a/fs/ntfs/lcnalloc.h
+++ b/fs/ntfs/lcnalloc.h
@@ -43,13 +43,14 @@ extern runlist_element *ntfs_cluster_all
const NTFS_CLUSTER_ALLOCATION_ZONES zone);
extern s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn,
- s64 count, const BOOL is_rollback);
+ s64 count, const BOOL write_locked, const BOOL is_rollback);
/**
* ntfs_cluster_free - free clusters on an ntfs volume
* @vi: vfs inode whose runlist describes the clusters to free
* @start_vcn: vcn in the runlist of @vi at which to start freeing clusters
* @count: number of clusters to free or -1 for all clusters
+ * @write_locked: true if the runlist is locked for writing
*
* Free @count clusters starting at the cluster @start_vcn in the runlist
* described by the vfs inode @vi.
@@ -64,19 +65,19 @@ extern s64 __ntfs_cluster_free(struct in
* Return the number of deallocated clusters (not counting sparse ones) on
* success and -errno on error.
*
- * Locking: - The runlist described by @vi must be unlocked on entry and is
- * unlocked on return.
- * - This function takes the runlist lock of @vi for reading and
- * sometimes for writing and sometimes modifies the runlist.
+ * Locking: - The runlist described by @vi must be locked on entry and is
+ * locked on return. Note if the runlist is locked for reading the
+ * lock may be dropped and reacquired. Note the runlist may be
+ * modified when needed runlist fragments need to be mapped.
* - The volume lcn bitmap must be unlocked on entry and is unlocked
* on return.
* - This function takes the volume lcn bitmap lock for writing and
* modifies the bitmap contents.
*/
static inline s64 ntfs_cluster_free(struct inode *vi, const VCN start_vcn,
- s64 count)
+ s64 count, const BOOL write_locked)
{
- return __ntfs_cluster_free(vi, start_vcn, count, FALSE);
+ return __ntfs_cluster_free(vi, start_vcn, count, write_locked, FALSE);
}
extern int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
@@ -93,8 +94,10 @@ extern int ntfs_cluster_free_from_rl_nol
*
* Return 0 on success and -errno on error.
*
- * Locking: This function takes the volume lcn bitmap lock for writing and
- * modifies the bitmap contents.
+ * Locking: - This function takes the volume lcn bitmap lock for writing and
+ * modifies the bitmap contents.
+ * - The caller must have locked the runlist @rl for reading or
+ * writing.
*/
static inline int ntfs_cluster_free_from_rl(ntfs_volume *vol,
const runlist_element *rl)
diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
--- a/fs/ntfs/mft.c
+++ b/fs/ntfs/mft.c
@@ -1953,7 +1953,7 @@ restore_undo_alloc:
a = ctx->attr;
a->data.non_resident.highest_vcn = cpu_to_sle64(old_last_vcn - 1);
undo_alloc:
- if (ntfs_cluster_free(vol->mft_ino, old_last_vcn, -1) < 0) {
+ if (ntfs_cluster_free(vol->mft_ino, old_last_vcn, -1, TRUE) < 0) {
ntfs_error(vol->sb, "Failed to free clusters from mft data "
"attribute.%s", es);
NVolSetErrors(vol);
[PATCH 18/25] NTFS: Make ntfs_write_block() not instantiate sparse blocks if they are zero.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2 ++
fs/ntfs/aops.c | 21 +++++++++++++++++++++
2 files changed, 23 insertions(+), 0 deletions(-)
8dcdebafb848415eae25924b00c4f0b9ec907da0
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -77,6 +77,8 @@ ToDo/Notes:
updating the times in the inode in ntfs_setattr().
- Fixup handling of sparse, compressed, and encrypted attributes in
fs/ntfs/inode.c::ntfs_read_locked_{,attr_,index_}inode().
+ - Make ntfs_write_block() not instantiate sparse blocks if they contain
+ only zeroes.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -670,6 +670,27 @@ lock_retry_remap:
}
/* It is a hole, need to instantiate it. */
if (lcn == LCN_HOLE) {
+ u8 *kaddr;
+ unsigned long *bpos, *bend;
+
+ /* Check if the buffer is zero. */
+ kaddr = kmap_atomic(page, KM_USER0);
+ bpos = (unsigned long *)(kaddr + bh_offset(bh));
+ bend = (unsigned long *)((u8*)bpos + blocksize);
+ do {
+ if (unlikely(*bpos))
+ break;
+ } while (likely(++bpos < bend));
+ kunmap_atomic(kaddr, KM_USER0);
+ if (bpos == bend) {
+ /*
+ * Buffer is zero and sparse, no need to write
+ * it.
+ */
+ bh->b_blocknr = -1;
+ clear_buffer_dirty(bh);
+ continue;
+ }
// TODO: Instantiate the hole.
// clear_buffer_new(bh);
// unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
[PATCH 16/25] NTFS: Truncate {a,c,m}time to the ntfs supported time granularity when
updating the times in the inode in ntfs_setattr().
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2 ++
fs/ntfs/inode.c | 12 +++++++-----
2 files changed, 9 insertions(+), 5 deletions(-)
1c7d469d47668f4664b892a6cd1c452a0c02d710
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -73,6 +73,8 @@ ToDo/Notes:
- Fix cluster (de)allocators to work when the runlist is NULL and more
importantly to take a locked runlist rather than them locking it
which leads to lock reversal.
+ - Truncate {a,c,m}time to the ntfs supported time granularity when
+ updating the times in the inode in ntfs_setattr().
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -2430,16 +2430,18 @@ int ntfs_setattr(struct dentry *dentry,
* We skipped the truncate but must still update
* timestamps.
*/
- ia_valid |= ATTR_MTIME|ATTR_CTIME;
+ ia_valid |= ATTR_MTIME | ATTR_CTIME;
}
}
-
if (ia_valid & ATTR_ATIME)
- vi->i_atime = attr->ia_atime;
+ vi->i_atime = timespec_trunc(attr->ia_atime,
+ vi->i_sb->s_time_gran);
if (ia_valid & ATTR_MTIME)
- vi->i_mtime = attr->ia_mtime;
+ vi->i_mtime = timespec_trunc(attr->ia_mtime,
+ vi->i_sb->s_time_gran);
if (ia_valid & ATTR_CTIME)
- vi->i_ctime = attr->ia_ctime;
+ vi->i_ctime = timespec_trunc(attr->ia_ctime,
+ vi->i_sb->s_time_gran);
mark_inode_dirty(vi);
out:
return err;
[PATCH 17/25] NTFS: Fixup handling of sparse, compressed, and encrypted attributes in
fs/ntfs/inode.c::ntfs_read_locked_{,attr_,index_}inode().
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2
fs/ntfs/inode.c | 215 ++++++++++++++++++++++++++++-------------------------
2 files changed, 116 insertions(+), 101 deletions(-)
67bb103725e4cde322cb4ddb160a12933c5c7072
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -75,6 +75,8 @@ ToDo/Notes:
which leads to lock reversal.
- Truncate {a,c,m}time to the ntfs supported time granularity when
updating the times in the inode in ntfs_setattr().
+ - Fixup handling of sparse, compressed, and encrypted attributes in
+ fs/ntfs/inode.c::ntfs_read_locked_{,attr_,index_}inode().
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -1013,41 +1013,50 @@ skip_large_dir_stuff:
}
a = ctx->attr;
/* Setup the state. */
- if (a->non_resident) {
- NInoSetNonResident(ni);
- if (a->flags & (ATTR_COMPRESSION_MASK |
- ATTR_IS_SPARSE)) {
- if (a->flags & ATTR_COMPRESSION_MASK) {
- NInoSetCompressed(ni);
- if (vol->cluster_size > 4096) {
- ntfs_error(vi->i_sb, "Found "
+ if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) {
+ if (a->flags & ATTR_COMPRESSION_MASK) {
+ NInoSetCompressed(ni);
+ if (vol->cluster_size > 4096) {
+ ntfs_error(vi->i_sb, "Found "
"compressed data but "
"compression is "
"disabled due to "
"cluster size (%i) > "
"4kiB.",
vol->cluster_size);
- goto unm_err_out;
- }
- if ((a->flags & ATTR_COMPRESSION_MASK)
- != ATTR_IS_COMPRESSED) {
- ntfs_error(vi->i_sb, "Found "
- "unknown compression "
- "method or corrupt "
- "file.");
- goto unm_err_out;
- }
+ goto unm_err_out;
+ }
+ if ((a->flags & ATTR_COMPRESSION_MASK)
+ != ATTR_IS_COMPRESSED) {
+ ntfs_error(vi->i_sb, "Found unknown "
+ "compression method "
+ "or corrupt file.");
+ goto unm_err_out;
}
- if (a->flags & ATTR_IS_SPARSE)
- NInoSetSparse(ni);
+ }
+ if (a->flags & ATTR_IS_SPARSE)
+ NInoSetSparse(ni);
+ }
+ if (a->flags & ATTR_IS_ENCRYPTED) {
+ if (NInoCompressed(ni)) {
+ ntfs_error(vi->i_sb, "Found encrypted and "
+ "compressed data.");
+ goto unm_err_out;
+ }
+ NInoSetEncrypted(ni);
+ }
+ if (a->non_resident) {
+ NInoSetNonResident(ni);
+ if (NInoCompressed(ni) || NInoSparse(ni)) {
if (a->data.non_resident.compression_unit !=
4) {
ntfs_error(vi->i_sb, "Found "
- "nonstandard compression unit "
- "(%u instead of 4). Cannot "
- "handle this.",
- a->data.non_resident.
- compression_unit);
+ "nonstandard "
+ "compression unit (%u "
+ "instead of 4). "
+ "Cannot handle this.",
+ a->data.non_resident.
+ compression_unit);
err = -EOPNOTSUPP;
goto unm_err_out;
}
@@ -1065,14 +1074,6 @@ skip_large_dir_stuff:
a->data.non_resident.
compressed_size);
}
- if (a->flags & ATTR_IS_ENCRYPTED) {
- if (a->flags & ATTR_COMPRESSION_MASK) {
- ntfs_error(vi->i_sb, "Found encrypted "
- "and compressed data.");
- goto unm_err_out;
- }
- NInoSetEncrypted(ni);
- }
if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of $DATA "
"attribute has non zero "
@@ -1212,6 +1213,75 @@ static int ntfs_read_locked_attr_inode(s
if (unlikely(err))
goto unm_err_out;
a = ctx->attr;
+ if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) {
+ if (a->flags & ATTR_COMPRESSION_MASK) {
+ NInoSetCompressed(ni);
+ if ((ni->type != AT_DATA) || (ni->type == AT_DATA &&
+ ni->name_len)) {
+ ntfs_error(vi->i_sb, "Found compressed "
+ "non-data or named data "
+ "attribute. Please report "
+ "you saw this message to "
+ "linux-ntfs-dev@lists."
+ "sourceforge.net");
+ goto unm_err_out;
+ }
+ if (vol->cluster_size > 4096) {
+ ntfs_error(vi->i_sb, "Found compressed "
+ "attribute but compression is "
+ "disabled due to cluster size "
+ "(%i) > 4kiB.",
+ vol->cluster_size);
+ goto unm_err_out;
+ }
+ if ((a->flags & ATTR_COMPRESSION_MASK) !=
+ ATTR_IS_COMPRESSED) {
+ ntfs_error(vi->i_sb, "Found unknown "
+ "compression method.");
+ goto unm_err_out;
+ }
+ }
+ /*
+ * The encryption flag set in an index root just means to
+ * compress all files.
+ */
+ if (NInoMstProtected(ni) && ni->type != AT_INDEX_ROOT) {
+ ntfs_error(vi->i_sb, "Found mst protected attribute "
+ "but the attribute is %s. Please "
+ "report you saw this message to "
+ "[email protected]",
+ NInoCompressed(ni) ? "compressed" :
+ "sparse");
+ goto unm_err_out;
+ }
+ if (a->flags & ATTR_IS_SPARSE)
+ NInoSetSparse(ni);
+ }
+ if (a->flags & ATTR_IS_ENCRYPTED) {
+ if (NInoCompressed(ni)) {
+ ntfs_error(vi->i_sb, "Found encrypted and compressed "
+ "data.");
+ goto unm_err_out;
+ }
+ /*
+ * The encryption flag set in an index root just means to
+ * encrypt all files.
+ */
+ if (NInoMstProtected(ni) && ni->type != AT_INDEX_ROOT) {
+ ntfs_error(vi->i_sb, "Found mst protected attribute "
+ "but the attribute is encrypted. "
+ "Please report you saw this message "
+ "to [email protected]."
+ "net");
+ goto unm_err_out;
+ }
+ if (ni->type != AT_DATA) {
+ ntfs_error(vi->i_sb, "Found encrypted non-data "
+ "attribute.");
+ goto unm_err_out;
+ }
+ NInoSetEncrypted(ni);
+ }
if (!a->non_resident) {
/* Ensure the attribute name is placed before the value. */
if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
@@ -1220,11 +1290,10 @@ static int ntfs_read_locked_attr_inode(s
"the attribute value.");
goto unm_err_out;
}
- if (NInoMstProtected(ni) || a->flags) {
+ if (NInoMstProtected(ni)) {
ntfs_error(vi->i_sb, "Found mst protected attribute "
- "or attribute with non-zero flags but "
- "the attribute is resident. Please "
- "report you saw this message to "
+ "but the attribute is resident. "
+ "Please report you saw this message to "
"[email protected]");
goto unm_err_out;
}
@@ -1250,50 +1319,8 @@ static int ntfs_read_locked_attr_inode(s
"the mapping pairs array.");
goto unm_err_out;
}
- if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) {
- if (a->flags & ATTR_COMPRESSION_MASK) {
- NInoSetCompressed(ni);
- if ((ni->type != AT_DATA) || (ni->type ==
- AT_DATA && ni->name_len)) {
- ntfs_error(vi->i_sb, "Found compressed "
- "non-data or named "
- "data attribute. "
- "Please report you "
- "saw this message to "
- "linux-ntfs-dev@lists."
- "sourceforge.net");
- goto unm_err_out;
- }
- if (vol->cluster_size > 4096) {
- ntfs_error(vi->i_sb, "Found compressed "
- "attribute but "
- "compression is "
- "disabled due to "
- "cluster size (%i) > "
- "4kiB.",
- vol->cluster_size);
- goto unm_err_out;
- }
- if ((a->flags & ATTR_COMPRESSION_MASK) !=
- ATTR_IS_COMPRESSED) {
- ntfs_error(vi->i_sb, "Found unknown "
- "compression method.");
- goto unm_err_out;
- }
- }
- if (NInoMstProtected(ni)) {
- ntfs_error(vi->i_sb, "Found mst protected "
- "attribute but the attribute "
- "is %s. Please report you "
- "saw this message to "
- "linux-ntfs-dev@lists."
- "sourceforge.net",
- NInoCompressed(ni) ?
- "compressed" : "sparse");
- goto unm_err_out;
- }
- if (a->flags & ATTR_IS_SPARSE)
- NInoSetSparse(ni);
+ if ((NInoCompressed(ni) || NInoSparse(ni)) &&
+ ni->type != AT_INDEX_ROOT) {
if (a->data.non_resident.compression_unit != 4) {
ntfs_error(vi->i_sb, "Found nonstandard "
"compression unit (%u instead "
@@ -1313,23 +1340,6 @@ static int ntfs_read_locked_attr_inode(s
ni->itype.compressed.size = sle64_to_cpu(
a->data.non_resident.compressed_size);
}
- if (a->flags & ATTR_IS_ENCRYPTED) {
- if (a->flags & ATTR_COMPRESSION_MASK) {
- ntfs_error(vi->i_sb, "Found encrypted and "
- "compressed data.");
- goto unm_err_out;
- }
- if (NInoMstProtected(ni)) {
- ntfs_error(vi->i_sb, "Found mst protected "
- "attribute but the attribute "
- "is encrypted. Please report "
- "you saw this message to "
- "linux-ntfs-dev@lists."
- "sourceforge.net");
- goto unm_err_out;
- }
- NInoSetEncrypted(ni);
- }
if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of attribute has "
"non-zero lowest_vcn.");
@@ -1348,12 +1358,12 @@ static int ntfs_read_locked_attr_inode(s
vi->i_mapping->a_ops = &ntfs_mst_aops;
else
vi->i_mapping->a_ops = &ntfs_aops;
- if (NInoCompressed(ni) || NInoSparse(ni))
+ if ((NInoCompressed(ni) || NInoSparse(ni)) && ni->type != AT_INDEX_ROOT)
vi->i_blocks = ni->itype.compressed.size >> 9;
else
vi->i_blocks = ni->allocated_size >> 9;
/*
- * Make sure the base inode doesn't go away and attach it to the
+ * Make sure the base inode does not go away and attach it to the
* attribute inode.
*/
igrab(base_vi);
@@ -1480,7 +1490,10 @@ static int ntfs_read_locked_index_inode(
"after the attribute value.");
goto unm_err_out;
}
- /* Compressed/encrypted/sparse index root is not allowed. */
+ /*
+ * Compressed/encrypted/sparse index root is not allowed, except for
+ * directories of course but those are not dealt with here.
+ */
if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED |
ATTR_IS_SPARSE)) {
ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index "
[PATCH 19/25] NTFS: Fixup handling of sparse, compressed, and encrypted attributes in
fs/ntfs/aops.c::ntfs_writepage().
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2 +
fs/ntfs/aops.c | 104 ++++++++++++++++++++++++-----------------------------
2 files changed, 49 insertions(+), 57 deletions(-)
bd45fdd209ca49c5010ac9af469c41ae6dd3f145
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -79,6 +79,8 @@ ToDo/Notes:
fs/ntfs/inode.c::ntfs_read_locked_{,attr_,index_}inode().
- Make ntfs_write_block() not instantiate sparse blocks if they contain
only zeroes.
+ - Fixup handling of sparse, compressed, and encrypted attributes in
+ fs/ntfs/aops.c::ntfs_writepage().
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -1301,38 +1301,42 @@ retry_writepage:
ntfs_debug("Write outside i_size - truncated?");
return 0;
}
+ /*
+ * Only $DATA attributes can be encrypted and only unnamed $DATA
+ * attributes can be compressed. Index root can have the flags set but
+ * this means to create compressed/encrypted files, not that the
+ * attribute is compressed/encrypted.
+ */
+ if (ni->type != AT_INDEX_ROOT) {
+ /* If file is encrypted, deny access, just like NT4. */
+ if (NInoEncrypted(ni)) {
+ unlock_page(page);
+ BUG_ON(ni->type != AT_DATA);
+ ntfs_debug("Denying write access to encrypted "
+ "file.");
+ return -EACCES;
+ }
+ /* Compressed data streams are handled in compress.c. */
+ if (NInoNonResident(ni) && NInoCompressed(ni)) {
+ BUG_ON(ni->type != AT_DATA);
+ BUG_ON(ni->name_len);
+ // TODO: Implement and replace this with
+ // return ntfs_write_compressed_block(page);
+ unlock_page(page);
+ ntfs_error(vi->i_sb, "Writing to compressed files is "
+ "not supported yet. Sorry.");
+ return -EOPNOTSUPP;
+ }
+ // TODO: Implement and remove this check.
+ if (NInoNonResident(ni) && NInoSparse(ni)) {
+ unlock_page(page);
+ ntfs_error(vi->i_sb, "Writing to sparse files is not "
+ "supported yet. Sorry.");
+ return -EOPNOTSUPP;
+ }
+ }
/* NInoNonResident() == NInoIndexAllocPresent() */
if (NInoNonResident(ni)) {
- /*
- * Only unnamed $DATA attributes can be compressed, encrypted,
- * and/or sparse.
- */
- if (ni->type == AT_DATA && !ni->name_len) {
- /* If file is encrypted, deny access, just like NT4. */
- if (NInoEncrypted(ni)) {
- unlock_page(page);
- ntfs_debug("Denying write access to encrypted "
- "file.");
- return -EACCES;
- }
- /* Compressed data streams are handled in compress.c. */
- if (NInoCompressed(ni)) {
- // TODO: Implement and replace this check with
- // return ntfs_write_compressed_block(page);
- unlock_page(page);
- ntfs_error(vi->i_sb, "Writing to compressed "
- "files is not supported yet. "
- "Sorry.");
- return -EOPNOTSUPP;
- }
- // TODO: Implement and remove this check.
- if (NInoSparse(ni)) {
- unlock_page(page);
- ntfs_error(vi->i_sb, "Writing to sparse files "
- "is not supported yet. Sorry.");
- return -EOPNOTSUPP;
- }
- }
/* We have to zero every time due to mmap-at-end-of-file. */
if (page->index >= (i_size >> PAGE_CACHE_SHIFT)) {
/* The page straddles i_size. */
@@ -1345,14 +1349,16 @@ retry_writepage:
/* Handle mst protected attributes. */
if (NInoMstProtected(ni))
return ntfs_write_mst_block(page, wbc);
- /* Normal data stream. */
+ /* Normal, non-resident data stream. */
return ntfs_write_block(page, wbc);
}
/*
- * Attribute is resident, implying it is not compressed, encrypted,
- * sparse, or mst protected. This also means the attribute is smaller
- * than an mft record and hence smaller than a page, so can simply
- * return error on any pages with index above 0.
+ * Attribute is resident, implying it is not compressed, encrypted, or
+ * mst protected. This also means the attribute is smaller than an mft
+ * record and hence smaller than a page, so can simply return error on
+ * any pages with index above 0. Note the attribute can actually be
+ * marked compressed but if it is resident the actual data is not
+ * compressed so we are ok to ignore the compressed flag here.
*/
BUG_ON(page_has_buffers(page));
BUG_ON(!PageUptodate(page));
@@ -1401,30 +1407,14 @@ retry_writepage:
BUG_ON(PageWriteback(page));
set_page_writeback(page);
unlock_page(page);
-
/*
- * Here, we don't need to zero the out of bounds area everytime because
- * the below memcpy() already takes care of the mmap-at-end-of-file
- * requirements. If the file is converted to a non-resident one, then
- * the code path use is switched to the non-resident one where the
- * zeroing happens on each ntfs_writepage() invocation.
- *
- * The above also applies nicely when i_size is decreased.
- *
- * When i_size is increased, the memory between the old and new i_size
- * _must_ be zeroed (or overwritten with new data). Otherwise we will
- * expose data to userspace/disk which should never have been exposed.
- *
- * FIXME: Ensure that i_size increases do the zeroing/overwriting and
- * if we cannot guarantee that, then enable the zeroing below. If the
- * zeroing below is enabled, we MUST move the unlock_page() from above
- * to after the kunmap_atomic(), i.e. just before the
- * end_page_writeback().
- * UPDATE: ntfs_prepare/commit_write() do the zeroing on i_size
- * increases for resident attributes so those are ok.
- * TODO: ntfs_truncate(), others?
+ * Here, we do not need to zero the out of bounds area everytime
+ * because the below memcpy() already takes care of the
+ * mmap-at-end-of-file requirements. If the file is converted to a
+ * non-resident one, then the code path use is switched to the
+ * non-resident one where the zeroing happens on each ntfs_writepage()
+ * invocation.
*/
-
attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
i_size = i_size_read(vi);
if (unlikely(attr_len > i_size)) {
[PATCH 21/25] NTFS: Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case
where a concurrent truncate has truncated the runlist under our feet.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 2 ++
fs/ntfs/aops.c | 51 ++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 42 insertions(+), 11 deletions(-)
8273d5d4c28a9fde68f830cc6ff61e37e8ae1dca
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -84,6 +84,8 @@ ToDo/Notes:
- Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page
lock protection over the buffer submission for i/o which allows the
removal of the get_bh()/put_bh() pairs for each buffer.
+ - Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case
+ where a concurrent truncate has truncated the runlist under our feet.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -204,6 +204,7 @@ static int ntfs_read_block(struct page *
nr = i = 0;
do {
u8 *kaddr;
+ int err;
if (unlikely(buffer_uptodate(bh)))
continue;
@@ -211,6 +212,7 @@ static int ntfs_read_block(struct page *
arr[nr++] = bh;
continue;
}
+ err = 0;
bh->b_bdev = vol->sb->s_bdev;
/* Is the block within the allowed limits? */
if (iblock < lblock) {
@@ -252,7 +254,6 @@ lock_retry_remap:
goto handle_hole;
/* If first try and runlist unmapped, map and retry. */
if (!is_retry && lcn == LCN_RL_NOT_MAPPED) {
- int err;
is_retry = TRUE;
/*
* Attempt to map runlist, dropping lock for
@@ -263,20 +264,30 @@ lock_retry_remap:
if (likely(!err))
goto lock_retry_remap;
rl = NULL;
- lcn = err;
} else if (!rl)
up_read(&ni->runlist.lock);
+ /*
+ * If buffer is outside the runlist, treat it as a
+ * hole. This can happen due to concurrent truncate
+ * for example.
+ */
+ if (err == -ENOENT || lcn == LCN_ENOENT) {
+ err = 0;
+ goto handle_hole;
+ }
/* Hard error, zero out region. */
+ if (!err)
+ err = -EIO;
bh->b_blocknr = -1;
SetPageError(page);
ntfs_error(vol->sb, "Failed to read from inode 0x%lx, "
"attribute type 0x%x, vcn 0x%llx, "
"offset 0x%x because its location on "
"disk could not be determined%s "
- "(error code %lli).", ni->mft_no,
+ "(error code %i).", ni->mft_no,
ni->type, (unsigned long long)vcn,
vcn_ofs, is_retry ? " even after "
- "retrying" : "", (long long)lcn);
+ "retrying" : "", err);
}
/*
* Either iblock was outside lblock limits or
@@ -289,9 +300,10 @@ handle_hole:
handle_zblock:
kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr + i * blocksize, 0, blocksize);
- flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
- set_buffer_uptodate(bh);
+ flush_dcache_page(page);
+ if (likely(!err))
+ set_buffer_uptodate(bh);
} while (i++, iblock++, (bh = bh->b_this_page) != head);
/* Release the lock if we took it. */
@@ -711,20 +723,37 @@ lock_retry_remap:
if (likely(!err))
goto lock_retry_remap;
rl = NULL;
- lcn = err;
} else if (!rl)
up_read(&ni->runlist.lock);
+ /*
+ * If buffer is outside the runlist, truncate has cut it out
+ * of the runlist. Just clean and clear the buffer and set it
+ * uptodate so it can get discarded by the VM.
+ */
+ if (err == -ENOENT || lcn == LCN_ENOENT) {
+ u8 *kaddr;
+
+ bh->b_blocknr = -1;
+ clear_buffer_dirty(bh);
+ kaddr = kmap_atomic(page, KM_USER0);
+ memset(kaddr + bh_offset(bh), 0, blocksize);
+ kunmap_atomic(kaddr, KM_USER0);
+ flush_dcache_page(page);
+ set_buffer_uptodate(bh);
+ err = 0;
+ continue;
+ }
/* Failed to map the buffer, even after retrying. */
+ if (!err)
+ err = -EIO;
bh->b_blocknr = -1;
ntfs_error(vol->sb, "Failed to write to inode 0x%lx, "
"attribute type 0x%x, vcn 0x%llx, offset 0x%x "
"because its location on disk could not be "
- "determined%s (error code %lli).", ni->mft_no,
+ "determined%s (error code %i).", ni->mft_no,
ni->type, (unsigned long long)vcn,
vcn_ofs, is_retry ? " even after "
- "retrying" : "", (long long)lcn);
- if (!err)
- err = -EIO;
+ "retrying" : "", err);
break;
} while (block++, (bh = bh->b_this_page) != head);
[PATCH 20/25] NTFS: Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page
lock protection over the buffer submission for i/o which allows the
removal of the get_bh()/put_bh() pairs for each buffer.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 3 +++
fs/ntfs/aops.c | 13 +++----------
2 files changed, 6 insertions(+), 10 deletions(-)
54b02eb01c0172294e43e2b54d6815f65637c111
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -81,6 +81,9 @@ ToDo/Notes:
only zeroes.
- Fixup handling of sparse, compressed, and encrypted attributes in
fs/ntfs/aops.c::ntfs_writepage().
+ - Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page
+ lock protection over the buffer submission for i/o which allows the
+ removal of the get_bh()/put_bh() pairs for each buffer.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -735,7 +735,7 @@ lock_retry_remap:
/* For the error case, need to reset bh to the beginning. */
bh = head;
- /* Just an optimization, so ->readpage() isn't called later. */
+ /* Just an optimization, so ->readpage() is not called later. */
if (unlikely(!PageUptodate(page))) {
int uptodate = 1;
do {
@@ -751,7 +751,6 @@ lock_retry_remap:
/* Setup all mapped, dirty buffers for async write i/o. */
do {
- get_bh(bh);
if (buffer_mapped(bh) && buffer_dirty(bh)) {
lock_buffer(bh);
if (test_clear_buffer_dirty(bh)) {
@@ -789,14 +788,8 @@ lock_retry_remap:
BUG_ON(PageWriteback(page));
set_page_writeback(page); /* Keeps try_to_free_buffers() away. */
- unlock_page(page);
- /*
- * Submit the prepared buffers for i/o. Note the page is unlocked,
- * and the async write i/o completion handler can end_page_writeback()
- * at any time after the *first* submit_bh(). So the buffers can then
- * disappear...
- */
+ /* Submit the prepared buffers for i/o. */
need_end_writeback = TRUE;
do {
struct buffer_head *next = bh->b_this_page;
@@ -804,9 +797,9 @@ lock_retry_remap:
submit_bh(WRITE, bh);
need_end_writeback = FALSE;
}
- put_bh(bh);
bh = next;
} while (bh != head);
+ unlock_page(page);
/* If no i/o was started, need to end_page_writeback(). */
if (unlikely(need_end_writeback))
[PATCH 23/25] NTFS: Fix page_has_buffers()/page_buffers() handling in fs/ntfs/aops.c.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 1 +
fs/ntfs/aops.c | 38 +++++++++++++++++++++-----------------
2 files changed, 22 insertions(+), 17 deletions(-)
a01ac532b519dc0e0b4d8bc4e12373e4e4cd1b1a
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -85,6 +85,7 @@ ToDo/Notes:
removal of the get_bh()/put_bh() pairs for each buffer.
- Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case
where a concurrent truncate has truncated the runlist under our feet.
+ - Fix page_has_buffers()/page_buffers() handling in fs/ntfs/aops.c.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -185,13 +185,15 @@ static int ntfs_read_block(struct page *
blocksize_bits = VFS_I(ni)->i_blkbits;
blocksize = 1 << blocksize_bits;
- if (!page_has_buffers(page))
+ if (!page_has_buffers(page)) {
create_empty_buffers(page, blocksize, 0);
- bh = head = page_buffers(page);
- if (unlikely(!bh)) {
- unlock_page(page);
- return -ENOMEM;
+ if (unlikely(!page_has_buffers(page))) {
+ unlock_page(page);
+ return -ENOMEM;
+ }
}
+ bh = head = page_buffers(page);
+ BUG_ON(!bh);
iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
read_lock_irqsave(&ni->size_lock, flags);
@@ -530,19 +532,21 @@ static int ntfs_write_block(struct page
BUG_ON(!PageUptodate(page));
create_empty_buffers(page, blocksize,
(1 << BH_Uptodate) | (1 << BH_Dirty));
+ if (unlikely(!page_has_buffers(page))) {
+ ntfs_warning(vol->sb, "Error allocating page "
+ "buffers. Redirtying page so we try "
+ "again later.");
+ /*
+ * Put the page back on mapping->dirty_pages, but leave
+ * its buffers' dirty state as-is.
+ */
+ redirty_page_for_writepage(wbc, page);
+ unlock_page(page);
+ return 0;
+ }
}
bh = head = page_buffers(page);
- if (unlikely(!bh)) {
- ntfs_warning(vol->sb, "Error allocating page buffers. "
- "Redirtying page so we try again later.");
- /*
- * Put the page back on mapping->dirty_pages, but leave its
- * buffer's dirty state as-is.
- */
- redirty_page_for_writepage(wbc, page);
- unlock_page(page);
- return 0;
- }
+ BUG_ON(!bh);
/* NOTE: Different naming scheme to ntfs_read_block()! */
@@ -910,7 +914,6 @@ static int ntfs_write_mst_block(struct p
sync = (wbc->sync_mode == WB_SYNC_ALL);
/* Make sure we have mapped buffers. */
- BUG_ON(!page_has_buffers(page));
bh = head = page_buffers(page);
BUG_ON(!bh);
@@ -2397,6 +2400,7 @@ void mark_ntfs_record_dirty(struct page
buffers_to_free = bh;
}
bh = head = page_buffers(page);
+ BUG_ON(!bh);
do {
bh_ofs = bh_offset(bh);
if (bh_ofs + bh_size <= ofs)
[PATCH 25/25] NTFS: 2.1.24 release and some minor final fixes.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
Documentation/filesystems/ntfs.txt | 12 ++++++++++++
fs/ntfs/ChangeLog | 4 +++-
fs/ntfs/Makefile | 2 +-
fs/ntfs/aops.c | 10 ++++------
fs/ntfs/super.c | 2 +-
5 files changed, 21 insertions(+), 9 deletions(-)
7d333d6c739a5cd6d60102ea1a9940cbbb0546ec
diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt
--- a/Documentation/filesystems/ntfs.txt
+++ b/Documentation/filesystems/ntfs.txt
@@ -439,6 +439,18 @@ ChangeLog
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
+2.1.24:
+ - Support journals ($LogFile) which have been modified by chkdsk. This
+ means users can boot into Windows after we marked the volume dirty.
+ The Windows boot will run chkdsk and then reboot. The user can then
+ immediately boot into Linux rather than having to do a full Windows
+ boot first before rebooting into Linux and we will recognize such a
+ journal and empty it as it is clean by definition.
+ - Support journals ($LogFile) with only one restart page as well as
+ journals with two different restart pages. We sanity check both and
+ either use the only sane one or the more recent one of the two in the
+ case that both are valid.
+ - Lots of bug fixes and enhancements across the board.
2.1.23:
- Stamp the user space journal, aka transaction log, aka $UsnJrnl, if
it is present and active thus telling Windows and applications using
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -22,7 +22,7 @@ ToDo/Notes:
- Enable the code for setting the NT4 compatibility flag when we start
making NTFS 1.2 specific modifications.
-2.1.24-WIP
+2.1.24 - Lots of bug fixes and support more clean journal states.
- Support journals ($LogFile) which have been modified by chkdsk. This
means users can boot into Windows after we marked the volume dirty.
@@ -89,6 +89,8 @@ ToDo/Notes:
- In fs/ntfs/aops.c::ntfs_end_buffer_async_read(), use a bit spin lock
in the first buffer head instead of a driver global spin lock to
improve scalability.
+ - Minor fix to error handling and error message display in
+ fs/ntfs/aops.c::ntfs_prepare_nonresident_write().
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile
--- a/fs/ntfs/Makefile
+++ b/fs/ntfs/Makefile
@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o c
index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
unistr.o upcase.o
-EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.24-WIP\"
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.24\"
ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -1727,27 +1727,25 @@ lock_retry_remap:
if (likely(!err))
goto lock_retry_remap;
rl = NULL;
- lcn = err;
} else if (!rl)
up_read(&ni->runlist.lock);
/*
* Failed to map the buffer, even after
* retrying.
*/
+ if (!err)
+ err = -EIO;
bh->b_blocknr = -1;
ntfs_error(vol->sb, "Failed to write to inode "
"0x%lx, attribute type 0x%x, "
"vcn 0x%llx, offset 0x%x "
"because its location on disk "
"could not be determined%s "
- "(error code %lli).",
+ "(error code %i).",
ni->mft_no, ni->type,
(unsigned long long)vcn,
vcn_ofs, is_retry ? " even "
- "after retrying" : "",
- (long long)lcn);
- if (!err)
- err = -EIO;
+ "after retrying" : "", err);
goto err_out;
}
/* We now have a successful remap, i.e. lcn >= 0. */
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1688,9 +1688,9 @@ static BOOL load_system_files(ntfs_volum
struct super_block *sb = vol->sb;
MFT_RECORD *m;
VOLUME_INFORMATION *vi;
- RESTART_PAGE_HEADER *rp;
ntfs_attr_search_ctx *ctx;
#ifdef NTFS_RW
+ RESTART_PAGE_HEADER *rp;
int err;
#endif /* NTFS_RW */
[PATCH 22/25] NTFS: Fixup handling of sparse, compressed, and encrypted attributes in
fs/ntfs/aops.c::ntfs_readpage().
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 5 ++---
fs/ntfs/aops.c | 41 ++++++++++++++++++++++++-----------------
2 files changed, 26 insertions(+), 20 deletions(-)
311120eca0013083f5eb0aff13ffb8aa9fdd050c
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -76,11 +76,10 @@ ToDo/Notes:
- Truncate {a,c,m}time to the ntfs supported time granularity when
updating the times in the inode in ntfs_setattr().
- Fixup handling of sparse, compressed, and encrypted attributes in
- fs/ntfs/inode.c::ntfs_read_locked_{,attr_,index_}inode().
+ fs/ntfs/inode.c::ntfs_read_locked_{,attr_,index_}inode(),
+ fs/ntfs/aops.c::ntfs_{read,write}page().
- Make ntfs_write_block() not instantiate sparse blocks if they contain
only zeroes.
- - Fixup handling of sparse, compressed, and encrypted attributes in
- fs/ntfs/aops.c::ntfs_writepage().
- Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page
lock protection over the buffer submission for i/o which allows the
removal of the get_bh()/put_bh() pairs for each buffer.
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -379,31 +379,38 @@ retry_readpage:
return 0;
}
ni = NTFS_I(page->mapping->host);
-
+ /*
+ * Only $DATA attributes can be encrypted and only unnamed $DATA
+ * attributes can be compressed. Index root can have the flags set but
+ * this means to create compressed/encrypted files, not that the
+ * attribute is compressed/encrypted.
+ */
+ if (ni->type != AT_INDEX_ROOT) {
+ /* If attribute is encrypted, deny access, just like NT4. */
+ if (NInoEncrypted(ni)) {
+ BUG_ON(ni->type != AT_DATA);
+ err = -EACCES;
+ goto err_out;
+ }
+ /* Compressed data streams are handled in compress.c. */
+ if (NInoNonResident(ni) && NInoCompressed(ni)) {
+ BUG_ON(ni->type != AT_DATA);
+ BUG_ON(ni->name_len);
+ return ntfs_read_compressed_block(page);
+ }
+ }
/* NInoNonResident() == NInoIndexAllocPresent() */
if (NInoNonResident(ni)) {
- /*
- * Only unnamed $DATA attributes can be compressed or
- * encrypted.
- */
- if (ni->type == AT_DATA && !ni->name_len) {
- /* If file is encrypted, deny access, just like NT4. */
- if (NInoEncrypted(ni)) {
- err = -EACCES;
- goto err_out;
- }
- /* Compressed data streams are handled in compress.c. */
- if (NInoCompressed(ni))
- return ntfs_read_compressed_block(page);
- }
- /* Normal data stream. */
+ /* Normal, non-resident data stream. */
return ntfs_read_block(page);
}
/*
* Attribute is resident, implying it is not compressed or encrypted.
* This also means the attribute is smaller than an mft record and
* hence smaller than a page, so can simply zero out any pages with
- * index above 0.
+ * index above 0. Note the attribute can actually be marked compressed
+ * but if it is resident the actual data is not compressed so we are
+ * ok to ignore the compressed flag here.
*/
if (unlikely(page->index > 0)) {
kaddr = kmap_atomic(page, KM_USER0);
[PATCH 24/25] NTFS: Improve scalability by changing the driver global spin lock in
fs/ntfs/aops.c::ntfs_end_buffer_async_read() to a bit spin lock
in the first buffer head of a page.
Signed-off-by: Anton Altaparmakov <[email protected]>
---
fs/ntfs/ChangeLog | 3 +++
fs/ntfs/aops.c | 15 +++++++++------
2 files changed, 12 insertions(+), 6 deletions(-)
e604635c8bea16f6177e6133eb3efbfb4a029ef6
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -86,6 +86,9 @@ ToDo/Notes:
- Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case
where a concurrent truncate has truncated the runlist under our feet.
- Fix page_has_buffers()/page_buffers() handling in fs/ntfs/aops.c.
+ - In fs/ntfs/aops.c::ntfs_end_buffer_async_read(), use a bit spin lock
+ in the first buffer head instead of a driver global spin lock to
+ improve scalability.
2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -55,9 +55,8 @@
*/
static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
{
- static DEFINE_SPINLOCK(page_uptodate_lock);
unsigned long flags;
- struct buffer_head *tmp;
+ struct buffer_head *first, *tmp;
struct page *page;
ntfs_inode *ni;
int page_uptodate = 1;
@@ -89,11 +88,13 @@ static void ntfs_end_buffer_async_read(s
}
} else {
clear_buffer_uptodate(bh);
+ SetPageError(page);
ntfs_error(ni->vol->sb, "Buffer I/O error, logical block %llu.",
(unsigned long long)bh->b_blocknr);
- SetPageError(page);
}
- spin_lock_irqsave(&page_uptodate_lock, flags);
+ first = page_buffers(page);
+ local_irq_save(flags);
+ bit_spin_lock(BH_Uptodate_Lock, &first->b_state);
clear_buffer_async_read(bh);
unlock_buffer(bh);
tmp = bh;
@@ -108,7 +109,8 @@ static void ntfs_end_buffer_async_read(s
}
tmp = tmp->b_this_page;
} while (tmp != bh);
- spin_unlock_irqrestore(&page_uptodate_lock, flags);
+ bit_spin_unlock(BH_Uptodate_Lock, &first->b_state);
+ local_irq_restore(flags);
/*
* If none of the buffers had errors then we can set the page uptodate,
* but we first have to perform the post read mst fixups, if the
@@ -141,7 +143,8 @@ static void ntfs_end_buffer_async_read(s
unlock_page(page);
return;
still_busy:
- spin_unlock_irqrestore(&page_uptodate_lock, flags);
+ bit_spin_unlock(BH_Uptodate_Lock, &first->b_state);
+ local_irq_restore(flags);
return;
}
On 9/9/05, Anton Altaparmakov <[email protected]> wrote:
> -static inline void *ntfs_malloc_nofs(unsigned long size)
> +static inline void *__ntfs_malloc(unsigned long size,
> + unsigned int __nocast gfp_mask)
> {
> if (likely(size <= PAGE_SIZE)) {
> BUG_ON(!size);
> /* kmalloc() has per-CPU caches so is faster for now. */
> - return kmalloc(PAGE_SIZE, GFP_NOFS);
> - /* return (void *)__get_free_page(GFP_NOFS | __GFP_HIGHMEM); */
> + return kmalloc(PAGE_SIZE, gfp_mask);
> + /* return (void *)__get_free_page(gfp_mask); */
> }
> if (likely(size >> PAGE_SHIFT < num_physpages))
> - return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
> + return __vmalloc(size, gfp_mask, PAGE_KERNEL);
Unrelated to this patch but why do you have this wrapper instead of
using kmalloc() where you can and__vmalloc() where you really have to?
Pekka
On Fri, 2005-09-09 at 13:36 +0300, Pekka Enberg wrote:
> On 9/9/05, Anton Altaparmakov <[email protected]> wrote:
> > -static inline void *ntfs_malloc_nofs(unsigned long size)
> > +static inline void *__ntfs_malloc(unsigned long size,
> > + unsigned int __nocast gfp_mask)
> > {
> > if (likely(size <= PAGE_SIZE)) {
> > BUG_ON(!size);
> > /* kmalloc() has per-CPU caches so is faster for now. */
> > - return kmalloc(PAGE_SIZE, GFP_NOFS);
> > - /* return (void *)__get_free_page(GFP_NOFS | __GFP_HIGHMEM); */
> > + return kmalloc(PAGE_SIZE, gfp_mask);
> > + /* return (void *)__get_free_page(gfp_mask); */
> > }
> > if (likely(size >> PAGE_SHIFT < num_physpages))
> > - return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
> > + return __vmalloc(size, gfp_mask, PAGE_KERNEL);
>
> Unrelated to this patch but why do you have this wrapper instead of
> using kmalloc() where you can and__vmalloc() where you really have to?
Very easy. Allocations are variable sized. Without the wrapper I would
have to copy and paste the wrapped code all over the ntfs driver as
there is no way to tell which one I would need in advance.
I used to simply use vmalloc() but that caused loads of people's
machines to run out of vmalloc space in a matter of hours and also
vmalloc is much slower so I added the kmalloc if a page and vmalloc
otherwise.
Note just using kmalloc is no good as it doesn't go high enough in size
(again this problem was being hit by people which is why I had switched
to vmalloc in the first place).
Also kmalloc with size > PAGE_SIZE used to cause machines to run OOM and
give page order > 1 allocation failures, hence why I never use kmalloc
for more than one page any more.
Also note I only use the ntfs_malloc_nofs() wrapper if I have to. If I
know how much I am allocating or at least know that the maximum is quite
small, I use kmalloc() directly. It is pretty much only for the runlist
allocations that I use the wrapper as the runlist is typically small but
for fragmented files it can grow huge. I have seen runlists consuming
over 256kiB of ram, without vmalloc that would be a real problem...
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
On Fri, 9 Sep 2005, Anton Altaparmakov wrote:
> Also note I only use the ntfs_malloc_nofs() wrapper if I have to. If I
> know how much I am allocating or at least know that the maximum is quite
> small, I use kmalloc() directly. It is pretty much only for the runlist
> allocations that I use the wrapper as the runlist is typically small but
> for fragmented files it can grow huge. I have seen runlists consuming
> over 256kiB of ram, without vmalloc that would be a real problem...
So things like
rl = ntfs_malloc_nofs(rlsize = PAGE_SIZE);
should be changed to kmalloc(), right?
Pekka
On Fri, 2005-09-09 at 14:15 +0300, Pekka J Enberg wrote:
> On Fri, 9 Sep 2005, Anton Altaparmakov wrote:
> > Also note I only use the ntfs_malloc_nofs() wrapper if I have to. If I
> > know how much I am allocating or at least know that the maximum is quite
> > small, I use kmalloc() directly. It is pretty much only for the runlist
> > allocations that I use the wrapper as the runlist is typically small but
> > for fragmented files it can grow huge. I have seen runlists consuming
> > over 256kiB of ram, without vmalloc that would be a real problem...
>
> So things like
>
> rl = ntfs_malloc_nofs(rlsize = PAGE_SIZE);
>
> should be changed to kmalloc(), right?
They could be but I would rather not. What if one day I decide to
change how ntfs_malloc_nofs() works? Then it would be needed to
carefully go through the whole driver looking for places where kmalloc
is used and change those, too.
>From a software design point of view you should never mix interfaces
when accessing an object if you want clean and maintainable code. And
using kmalloc() sometimes and ntfs_malloc_nofs() at other times for the
same object would violate that.
The wrapper is a static inline so I would assume gcc can optimize away
everything when a constant size is passed in like in the example you
point out above.
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
On Fri, 9 Sep 2005, Anton Altaparmakov wrote:
> They could be but I would rather not. What if one day I decide to
> change how ntfs_malloc_nofs() works? Then it would be needed to
> carefully go through the whole driver looking for places where kmalloc
> is used and change those, too.
>
> From a software design point of view you should never mix interfaces
> when accessing an object if you want clean and maintainable code. And
> using kmalloc() sometimes and ntfs_malloc_nofs() at other times for the
> same object would violate that.
>
> The wrapper is a static inline so I would assume gcc can optimize away
> everything when a constant size is passed in like in the example you
> point out above.
Hey, I am not worried about performance. It's just that filesystems (or
any other subsystem for that matter) should not invent their own memory
allocators. Perhaps should provide a generic __vmalloc_fast() if this is
really required?
Pekka
On Fri, 2005-09-09 at 14:38 +0300, Pekka J Enberg wrote:
> On Fri, 9 Sep 2005, Anton Altaparmakov wrote:
> > They could be but I would rather not. What if one day I decide to
> > change how ntfs_malloc_nofs() works? Then it would be needed to
> > carefully go through the whole driver looking for places where kmalloc
> > is used and change those, too.
> >
> > From a software design point of view you should never mix interfaces
> > when accessing an object if you want clean and maintainable code. And
> > using kmalloc() sometimes and ntfs_malloc_nofs() at other times for the
> > same object would violate that.
> >
> > The wrapper is a static inline so I would assume gcc can optimize away
> > everything when a constant size is passed in like in the example you
> > point out above.
>
> Hey, I am not worried about performance. It's just that filesystems (or
> any other subsystem for that matter) should not invent their own memory
> allocators. Perhaps should provide a generic __vmalloc_fast() if this is
> really required?
Even if that were the case I would still use a wrapper. I am far too
lazy to write __vmalloc(x, GFP_NOFS | __GFP_HIGHMEM); or even
__vmalloc(x, GFP_NOFS | __GFP_HIGHMEM | __GFP_NOFAIL); when I can get
away with ntfs_malloc_nofs{,nofail}()... (-;
I completely disagree with you given that this is not "inventing [...]
own memory allocators", it is just a convenient short hand. I am sure a
lot of people would agree with you though. It is just a matter of
personal preference.
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
On Fri, 2005-09-09 at 12:48 +0100, Anton Altaparmakov wrote:
> On Fri, 2005-09-09 at 14:38 +0300, Pekka J Enberg wrote:
> > On Fri, 9 Sep 2005, Anton Altaparmakov wrote:
> > > They could be but I would rather not. What if one day I decide to
> > > change how ntfs_malloc_nofs() works? Then it would be needed to
> > > carefully go through the whole driver looking for places where kmalloc
> > > is used and change those, too.
> > >
> > > From a software design point of view you should never mix interfaces
> > > when accessing an object if you want clean and maintainable code. And
> > > using kmalloc() sometimes and ntfs_malloc_nofs() at other times for the
> > > same object would violate that.
> > >
> > > The wrapper is a static inline so I would assume gcc can optimize away
> > > everything when a constant size is passed in like in the example you
> > > point out above.
> >
> > Hey, I am not worried about performance. It's just that filesystems (or
> > any other subsystem for that matter) should not invent their own memory
> > allocators. Perhaps should provide a generic __vmalloc_fast() if this is
> > really required?
>
> Even if that were the case I would still use a wrapper. I am far too
> lazy to write __vmalloc(x, GFP_NOFS | __GFP_HIGHMEM); or even
> __vmalloc(x, GFP_NOFS | __GFP_HIGHMEM | __GFP_NOFAIL); when I can get
> away with ntfs_malloc_nofs{,nofail}()... (-;
>
> I completely disagree with you given that this is not "inventing [...]
> own memory allocators", it is just a convenient short hand. I am sure a
> lot of people would agree with you though. It is just a matter of
> personal preference.
I should add that this is not ntfs only, the idea is from another file
system which uses it, too. Can't remember which one it was, though (xfs
maybe?).
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
On Fri, 9 Sep 2005, Anton Altaparmakov wrote:
> > I completely disagree with you given that this is not "inventing [...]
> > own memory allocators", it is just a convenient short hand. I am sure a
> > lot of people would agree with you though. It is just a matter of
> > personal preference.
>
> I should add that this is not ntfs only, the idea is from another file
> system which uses it, too. Can't remember which one it was, though (xfs
> maybe?).
Indeed. It is not just a matter of personal preference but also a matter
of subsystems introducing duplicate code like this. Quick grepping shows
UDF doing same thing and XFS doing slightly differently but I am pretty
sure I've seen it elsewhere too.
Pekka
On Fri, 2005-09-09 at 15:02 +0300, Pekka J Enberg wrote:
> On Fri, 9 Sep 2005, Anton Altaparmakov wrote:
> > > I completely disagree with you given that this is not "inventing [...]
> > > own memory allocators", it is just a convenient short hand. I am sure a
> > > lot of people would agree with you though. It is just a matter of
> > > personal preference.
> >
> > I should add that this is not ntfs only, the idea is from another file
> > system which uses it, too. Can't remember which one it was, though (xfs
> > maybe?).
>
> Indeed. It is not just a matter of personal preference but also a matter
> of subsystems introducing duplicate code like this. Quick grepping shows
> UDF doing same thing and XFS doing slightly differently but I am pretty
> sure I've seen it elsewhere too.
Yes, that is usually a good indication that a generic function should be
provided. However having a generic function with complicated and long
arguments is no use as everyone will want their own shorter one anyway.
And given the function is static inline it actually makes no difference
to the generated code size. Also calling it __vmalloc_fast makes no
sense as it doesn't always use vmalloc... Given we have kmalloc and
vmalloc maybe it should be just malloc?
Obviously if there were a suitable generic function I would use it but I
and I imagine all the other users would still wrap it with the old name.
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
Anton> fs/ntfs/malloc.h::ntfs_malloc_nofs() to do the kmalloc()
Anton> based allocations with __GFP_HIGHMEM, analogous to how the
Anton> vmalloc() based allocations are done.
Does it make sense to pass __GFP_HIGHMEM to kmalloc()? kmalloc() has
to return memory from lowmem, since it gives you an address from the
direct-mapped kernel area, so at best kmalloc() ignores this flag.
- R.
On Fri, Sep 09, 2005 at 07:51:23AM -0700, Roland Dreier wrote:
> Anton> fs/ntfs/malloc.h::ntfs_malloc_nofs() to do the kmalloc()
> Anton> based allocations with __GFP_HIGHMEM, analogous to how the
> Anton> vmalloc() based allocations are done.
>
> Does it make sense to pass __GFP_HIGHMEM to kmalloc()?
Not at all (as you indicated below..)
> kmalloc() has
> to return memory from lowmem, since it gives you an address from the
> direct-mapped kernel area, so at best kmalloc() ignores this flag.
On Fri, 2005-09-09 at 15:53 +0100, Christoph Hellwig wrote:
> On Fri, Sep 09, 2005 at 07:51:23AM -0700, Roland Dreier wrote:
> > Anton> fs/ntfs/malloc.h::ntfs_malloc_nofs() to do the kmalloc()
> > Anton> based allocations with __GFP_HIGHMEM, analogous to how the
> > Anton> vmalloc() based allocations are done.
> >
> > Does it make sense to pass __GFP_HIGHMEM to kmalloc()?
>
> Not at all (as you indicated below..)
>
> > kmalloc() has
> > to return memory from lowmem, since it gives you an address from the
> > direct-mapped kernel area, so at best kmalloc() ignores this flag.
Correct, but it doesn't do any harm either AFAICS (it gets masked out by
kmalloc) and it makes the ntfs code simpler since I can then use the
same gfp_flags parameter both for the kmalloc and the vmalloc rather
than needing two different ones.
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
Anton Altaparmakov <[email protected]> wrote:
> On Fri, 2005-09-09 at 15:02 +0300, Pekka J Enberg wrote:
> > On Fri, 9 Sep 2005, Anton Altaparmakov wrote:
> > > > I completely disagree with you given that this is not "inventing
> > > > [...] own memory allocators", it is just a convenient short hand.
> > > > I am sure a lot of people would agree with you though. It is just
> > > > a matter of personal preference.
> > > I should add that this is not ntfs only, the idea is from another file
> > > system which uses it, too. Can't remember which one it was, though (xfs
> > > maybe?).
> > Indeed. It is not just a matter of personal preference but also a matter
> > of subsystems introducing duplicate code like this. Quick grepping shows
> > UDF doing same thing and XFS doing slightly differently but I am pretty
> > sure I've seen it elsewhere too.
> Yes, that is usually a good indication that a generic function should be
> provided. However having a generic function with complicated and long
> arguments is no use as everyone will want their own shorter one anyway.
> And given the function is static inline it actually makes no difference
> to the generated code size. Also calling it __vmalloc_fast makes no
> sense as it doesn't always use vmalloc... Given we have kmalloc and
> vmalloc maybe it should be just malloc?
That it makes no difference to the generated code isn't enough. If somebody
if going over the kernel with a fine comb trying to fix memory allocations
(example as here), she will have to remember half a dozen slightly
different ways of doing the same thing, for no good reason. And probably
miss a bundle in the process. This is bad in itself.
> Obviously if there were a suitable generic function I would use it but I
> and I imagine all the other users would still wrap it with the old name.
I'd hope not.
[Yes, I can understand that each subsystem has its own "culture", and
trying to force them all into the same mold is friction too.]
--
Dr. Horst H. von Brand User #22616 counter.li.org
Departamento de Informatica Fono: +56 32 654431
Universidad Tecnica Federico Santa Maria +56 32 654239
Casilla 110-V, Valparaiso, Chile Fax: +56 32 797513
On Fri, 9 Sep 2005 10:18:01 +0100 (BST), Anton Altaparmakov wrote:
> Hi Linus, please pull from
>
> rsync://rsync.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs-2.6.git/HEAD
>
> This is the next NTFS update containing a ton of bug fixes several of
> which fix bugs people actually hit in the big bad world...
>
> Please apply. Thanks!
>
> I am sending the changesets as actual patches generated using git
> format-patch for non-git users in follow up emails (in reply to this one).
>
> Best regards,
>
> Anton
BTW Anton, while looking for the best permission masks to be used when
mounting my NTFS paritions, I spotted what I think is a bug, or at
least an inconsistency between the way all fs drivers I use handle
umasks & friends, and the way NTFS does it. Basically, all the other
fs drivers take an octal representation of the masks. NTFS, instead,
seems to use _decimal_
(Of course, the spontaneous question is how comes that there isn't a
common fs code to read the common options?)
--
Giuseppe "Oblomov" Bilotta
Hic manebimus optime
On Friday 09 September 2005 10:18, Anton Altaparmakov wrote:
> Hi Linus, please pull from
>
> rsync://rsync.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs-2.6.git/HEAD
>
> This is the next NTFS update containing a ton of bug fixes several of
> which fix bugs people actually hit in the big bad world...
>
> Please apply. Thanks!
>
> I am sending the changesets as actual patches generated using git
> format-patch for non-git users in follow up emails (in reply to this one).
>
Anton,
Do these changes allow us to mount an NTFS volume created by Windows
Vista/Longhorn beta 1 yet? I tried the driver in 2.6.13, and it complains
about these $LogFile states, and ntfscp refuses to work.
If you're unaware of the problem, I'm happy to help debug it.
--
Cheers,
Alistair.
'No sense being pessimistic, it probably wouldn't work anyway.'
Third year Computer Science undergraduate.
1F2 55 South Clerk Street, Edinburgh, UK.
On Sat, 10 Sep 2005, Alistair John Strachan wrote:
> On Friday 09 September 2005 10:18, Anton Altaparmakov wrote:
> > Hi Linus, please pull from
> >
> > rsync://rsync.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs-2.6.git/HEAD
> >
> > This is the next NTFS update containing a ton of bug fixes several of
> > which fix bugs people actually hit in the big bad world...
> >
> > Please apply. Thanks!
> >
> > I am sending the changesets as actual patches generated using git
> > format-patch for non-git users in follow up emails (in reply to this one).
>
> Do these changes allow us to mount an NTFS volume created by Windows
> Vista/Longhorn beta 1 yet? I tried the driver in 2.6.13, and it complains
> about these $LogFile states, and ntfscp refuses to work.
>
> If you're unaware of the problem, I'm happy to help debug it.
I am indeed unaware of the problem. Could you try the latest kernel with
the ntfs patches in it (Linus already merged them in the official git
tree) and tell me if it now works? Thanks a lot in advance!
Note you will need to try the ntfs driver itself and not ntfscp as libntfs
does not have these changes yet hence ntfscp will not work just the same
(it does not use the kernel driver at all, it only uses libntfs).
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
On Sat, 10 Sep 2005, Giuseppe Bilotta wrote:
> On Fri, 9 Sep 2005 10:18:01 +0100 (BST), Anton Altaparmakov wrote:
> > This is the next NTFS update containing a ton of bug fixes several of
> > which fix bugs people actually hit in the big bad world...
> >
> > Please apply. Thanks!
> >
> > I am sending the changesets as actual patches generated using git
> > format-patch for non-git users in follow up emails (in reply to this one).
>
> BTW Anton, while looking for the best permission masks to be used when
> mounting my NTFS paritions, I spotted what I think is a bug, or at
> least an inconsistency between the way all fs drivers I use handle
> umasks & friends, and the way NTFS does it. Basically, all the other
> fs drivers take an octal representation of the masks. NTFS, instead,
> seems to use _decimal_
NTFS takes any. It is happy with octal, decimal, and hex. The ntfs
driver uses linux/lib/vsprintf.c::simple_strtoul() with a zero base which
autodetects which base to use so if you use umask=0222 it will take this
as octal and if you use umask=222 it will take this as decimal and if you
use 0x222 it will take this as decimal.
I do not see what is wrong with that. It behaves exactly like I would
expect it to. Maybe I have strange expectations? (-;
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
On Sat, 10 Sep 2005, Anton Altaparmakov wrote:
> On Sat, 10 Sep 2005, Giuseppe Bilotta wrote:
> > On Fri, 9 Sep 2005 10:18:01 +0100 (BST), Anton Altaparmakov wrote:
> > > This is the next NTFS update containing a ton of bug fixes several of
> > > which fix bugs people actually hit in the big bad world...
> > >
> > > Please apply. Thanks!
> > >
> > > I am sending the changesets as actual patches generated using git
> > > format-patch for non-git users in follow up emails (in reply to this one).
> >
> > BTW Anton, while looking for the best permission masks to be used when
> > mounting my NTFS paritions, I spotted what I think is a bug, or at
> > least an inconsistency between the way all fs drivers I use handle
> > umasks & friends, and the way NTFS does it. Basically, all the other
> > fs drivers take an octal representation of the masks. NTFS, instead,
> > seems to use _decimal_
>
> NTFS takes any. It is happy with octal, decimal, and hex. The ntfs
> driver uses linux/lib/vsprintf.c::simple_strtoul() with a zero base which
> autodetects which base to use so if you use umask=0222 it will take this
> as octal and if you use umask=222 it will take this as decimal and if you
> use 0x222 it will take this as decimal.
^^^^^^^ hexadecimal
> I do not see what is wrong with that. It behaves exactly like I would
> expect it to. Maybe I have strange expectations? (-;
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
In article <[email protected]> you wrote:
> I do not see what is wrong with that. It behaves exactly like I would
> expect it to. Maybe I have strange expectations? (-;
Well, the others bahave differently, so we need to fix all others.
Greetings
Bernd
On Sat, 10 Sep 2005 14:28:49 +0100 (BST), Anton Altaparmakov wrote:
> NTFS takes any. It is happy with octal, decimal, and hex. The ntfs
> driver uses linux/lib/vsprintf.c::simple_strtoul() with a zero base which
> autodetects which base to use so if you use umask=0222 it will take this
> as octal and if you use umask=222 it will take this as decimal and if you
> use 0x222 it will take this as decimal.
Uh. I was pretty sure I had tried the 0 prefix to get octal but it
failed, IIRC. Maybe I didn't run the test correctly, though.
> I do not see what is wrong with that. It behaves exactly like I would
> expect it to. Maybe I have strange expectations? (-;
I don't actually care one way or the other. I was just hoping for more
consistency across drivers :) This is why I went as far as suggesting
code sharing.
--
Giuseppe "Oblomov" Bilotta
"E la storia dell'umanit?, babbo?"
"Ma niente: prima si fanno delle cazzate,
poi si studia che cazzate si sono fatte"
(Altan)
("And what about the history of the human race, dad?"
"Oh, nothing special: first they make some foolish things,
then you study what foolish things have been made")
Anton Altaparmakov <[email protected]> wrote:
> On Sat, 10 Sep 2005, Giuseppe Bilotta wrote:
[...]
> > BTW Anton, while looking for the best permission masks to be used when
> > mounting my NTFS paritions, I spotted what I think is a bug, or at
> > least an inconsistency between the way all fs drivers I use handle
> > umasks & friends, and the way NTFS does it. Basically, all the other
> > fs drivers take an octal representation of the masks. NTFS, instead,
> > seems to use _decimal_
> NTFS takes any. It is happy with octal, decimal, and hex. The ntfs
> driver uses linux/lib/vsprintf.c::simple_strtoul() with a zero base which
> autodetects which base to use so if you use umask=0222 it will take this
> as octal and if you use umask=222 it will take this as decimal and if you
> use 0x222 it will take this as decimal.
> I do not see what is wrong with that. It behaves exactly like I would
> expect it to. Maybe I have strange expectations? (-;
At least chmod(1) takes /only/ octal, so 666 isn't the number of the beast,
but plain rw for everybody ;-)
I think this should be consistent with that.
--
Dr. Horst H. von Brand User #22616 counter.li.org
Departamento de Informatica Fono: +56 32 654431
Universidad Tecnica Federico Santa Maria +56 32 654239
Casilla 110-V, Valparaiso, Chile Fax: +56 32 797513
On Sun, 11 Sep 2005, Horst von Brand wrote:
> Anton Altaparmakov <[email protected]> wrote:
> > On Sat, 10 Sep 2005, Giuseppe Bilotta wrote:
> [...]
> > > BTW Anton, while looking for the best permission masks to be used when
> > > mounting my NTFS paritions, I spotted what I think is a bug, or at
> > > least an inconsistency between the way all fs drivers I use handle
> > > umasks & friends, and the way NTFS does it. Basically, all the other
> > > fs drivers take an octal representation of the masks. NTFS, instead,
> > > seems to use _decimal_
>
> > NTFS takes any. It is happy with octal, decimal, and hex. The ntfs
> > driver uses linux/lib/vsprintf.c::simple_strtoul() with a zero base which
> > autodetects which base to use so if you use umask=0222 it will take this
> > as octal and if you use umask=222 it will take this as decimal and if you
> > use 0x222 it will take this as decimal.
>
> > I do not see what is wrong with that. It behaves exactly like I would
> > expect it to. Maybe I have strange expectations? (-;
>
> At least chmod(1) takes /only/ octal, so 666 isn't the number of the beast,
> but plain rw for everybody ;-)
Ah, oops. I don't think I ever noticed that. I always have given it
0xyz, i.e. "proper" octal.
> I think this should be consistent with that.
Yes, I fully agree. It should be consistent. I will patch ntfs and send
it to Linus for 2.6.14.
Thanks to both of you for pointing this out.
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
On Sat, 10 Sep 2005, Anton Altaparmakov wrote:
> On Sat, 10 Sep 2005, Alistair John Strachan wrote:
> >
> > Do these changes allow us to mount an NTFS volume created by Windows
> > Vista/Longhorn beta 1 yet?
So far the current NTFS code worked fine with Longhorn/Vista releases.
WinFS and TxFS (transactional NTFS) are on top of current NTFS and MS
engineers claim that they work hard to avoid on-disk NTFS format changes.
> > I tried the driver in 2.6.13, and it complains about these
> > $LogFile states, and ntfscp refuses to work.
This happens probably due to the recent, strict $LogFile check changes.
Several people reported these and Anton is investigating it.
> > If you're unaware of the problem, I'm happy to help debug it.
Anybody [keep] confirming that the current Linux NTFS code still works
on the latest Vista beta releases would be highly encouraging for all
of us ;)
> I am indeed unaware of the problem. Could you try the latest kernel
> with the ntfs patches in it (Linus already merged them in the official
> git tree) and tell me if it now works? Thanks a lot in advance!
Alistair, any result?
> Note you will need to try the ntfs driver itself and not ntfscp as libntfs
> does not have these changes yet hence ntfscp will not work just the same
> (it does not use the kernel driver at all, it only uses libntfs).
The latest ntfsprogs CVS has also these changes and every tool should work
fine with Vista (ntfscp, ntfsresize, ntfsundelete, ntfsclone, etc).
Szaka
On Sunday 25 September 2005 20:12, Szakacsits Szabolcs wrote:
> On Sat, 10 Sep 2005, Anton Altaparmakov wrote:
> > On Sat, 10 Sep 2005, Alistair John Strachan wrote:
> > > Do these changes allow us to mount an NTFS volume created by Windows
> > > Vista/Longhorn beta 1 yet?
>
> So far the current NTFS code worked fine with Longhorn/Vista releases.
> WinFS and TxFS (transactional NTFS) are on top of current NTFS and MS
> engineers claim that they work hard to avoid on-disk NTFS format changes.
>
> > > I tried the driver in 2.6.13, and it complains about these
> > > $LogFile states, and ntfscp refuses to work.
>
> This happens probably due to the recent, strict $LogFile check changes.
> Several people reported these and Anton is investigating it.
>
> > > If you're unaware of the problem, I'm happy to help debug it.
>
> Anybody [keep] confirming that the current Linux NTFS code still works
> on the latest Vista beta releases would be highly encouraging for all
> of us ;)
>
> > I am indeed unaware of the problem. Could you try the latest kernel
> > with the ntfs patches in it (Linus already merged them in the official
> > git tree) and tell me if it now works? Thanks a lot in advance!
>
> Alistair, any result?
>
> > Note you will need to try the ntfs driver itself and not ntfscp as
> > libntfs does not have these changes yet hence ntfscp will not work just
> > the same (it does not use the kernel driver at all, it only uses
> > libntfs).
>
> The latest ntfsprogs CVS has also these changes and every tool should work
> fine with Vista (ntfscp, ntfsresize, ntfsundelete, ntfsclone, etc).
I have limited access to the beta, as it expires every 30 days and forces me
to reinstall it. I promise to get back to all of you after 2.6.14 is released
with the LogFile changes.
To clarify, I did not leave the Vista NTFS volume in an inconsistent state. I
even forced a chkdsk, rebooted, let it run through, then attempted again to
mount it with the NTFS code in 2.6.13. This categorically fails.
--
Cheers,
Alistair.
'No sense being pessimistic, it probably wouldn't work anyway.'
Third year Computer Science undergraduate.
1F2 55 South Clerk Street, Edinburgh, UK.
On Sun, 25 Sep 2005, Alistair John Strachan wrote:
> I have limited access to the beta, as it expires every 30 days and forces
> me to reinstall it. I promise to get back to all of you after 2.6.14 is
> released with the LogFile changes.
Thanks. Also, if you don't want to patch or compile yourself now then you
could just download and unpack this latest CVS ntfsclone binary (used by us
to collect NTFS metadata images for investigation):
http://linux-ntfs.sf.net/snapshots/ntfsclone-static-1.11.3-WIP2.tgz
and run e.g.
ntfsclone --metadata --output vista-ntfs.img <partition>
If it gives an error then rerun with the --debug option and the end of the
output will tell what's the reason for the failure.
> To clarify, I did not leave the Vista NTFS volume in an inconsistent
> state. I even forced a chkdsk, rebooted, let it run through, then
> attempted again to mount it with the NTFS code in 2.6.13. This
> categorically fails.
Currently this is the expected behavior if you "interrupt" the chkdsk
procedure. If you schedule chkdsk then you must boot Windows twice, not
only once. Otherwise you get the below error message
NTFS error: ntfs_check_logfile(): Did not find any restart pages in
$LogFile and it was not empty.
which means that the log was used by chkdsk which will be handled by the
next Windows boot. At present the Linux NTFS code can't cope with this
scenario so you must boot Windows again and then Linux. If you did so, then
well, the above command could tell the reason.
BTW, this is not Vista only feature.
Szaka
On Sunday 25 September 2005 23:35, Alistair John Strachan wrote:
[snip]
> >
> > Alistair, any result?
> >
> > > Note you will need to try the ntfs driver itself and not ntfscp as
> > > libntfs does not have these changes yet hence ntfscp will not work just
> > > the same (it does not use the kernel driver at all, it only uses
> > > libntfs).
> >
> > The latest ntfsprogs CVS has also these changes and every tool should
> > work fine with Vista (ntfscp, ntfsresize, ntfsundelete, ntfsclone, etc).
>
> I have limited access to the beta, as it expires every 30 days and forces
> me to reinstall it. I promise to get back to all of you after 2.6.14 is
> released with the LogFile changes.
>
> To clarify, I did not leave the Vista NTFS volume in an inconsistent state.
> I even forced a chkdsk, rebooted, let it run through, then attempted again
> to mount it with the NTFS code in 2.6.13. This categorically fails.
I was free today, so I built a 2.6.14-rc4 kernel on the machine with the
Longhorn NTFS volume. It now mounts without warnings in dmesg, and I've
verified that ntfscp works properly.
Thanks!
--
Cheers,
Alistair.
'No sense being pessimistic, it probably wouldn't work anyway.'
Third year Computer Science undergraduate.
1F2 55 South Clerk Street, Edinburgh, UK.
On Thu, 2005-10-13 at 16:13 +0100, Alistair John Strachan wrote:
> On Sunday 25 September 2005 23:35, Alistair John Strachan wrote:
> [snip]
> > >
> > > Alistair, any result?
> > >
> > > > Note you will need to try the ntfs driver itself and not ntfscp as
> > > > libntfs does not have these changes yet hence ntfscp will not work just
> > > > the same (it does not use the kernel driver at all, it only uses
> > > > libntfs).
> > >
> > > The latest ntfsprogs CVS has also these changes and every tool should
> > > work fine with Vista (ntfscp, ntfsresize, ntfsundelete, ntfsclone, etc).
> >
> > I have limited access to the beta, as it expires every 30 days and forces
> > me to reinstall it. I promise to get back to all of you after 2.6.14 is
> > released with the LogFile changes.
> >
> > To clarify, I did not leave the Vista NTFS volume in an inconsistent state.
> > I even forced a chkdsk, rebooted, let it run through, then attempted again
> > to mount it with the NTFS code in 2.6.13. This categorically fails.
>
> I was free today, so I built a 2.6.14-rc4 kernel on the machine with the
> Longhorn NTFS volume. It now mounts without warnings in dmesg, and I've
> verified that ntfscp works properly.
Great! Thanks for letting us know!
Best regards,
Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/