Commit 639b1a19 authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: 2.1.15 - Implement fs/ntfs/aops.c::ntfs_write_mst_block() which

      enables the writing of page cache pages belonging to mst protected
      attributes like the index allocation attribute in directory indices
      and other indices like $Quota/$Q, etc.  This means that the quota is
      now marked out of date on all volumes rather than only on ones where
      the quota defaults entry is in the index root attribute of the
      $Quota/$Q index.
Signed-off-by: default avatarAnton Altaparmakov <aia21@cantab.net>
parent db14fecd
......@@ -273,6 +273,10 @@ ChangeLog
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
2.1.15:
- Invalidate quotas when (re)mounting read-write.
NOTE: This now only leave user space journalling on the side. (See
note for version 2.1.13, below.)
2.1.14:
- Fix an NFSd caused deadlock reported by several users.
2.1.13:
......
ToDo/Notes:
- Find and fix bugs.
- Either invalidate quotas or update the quota charges on NTFS 3.x
volumes with quota tracking enabled ($Quota).
- Checkpoint or disable the user space journal ($UsnJrnl).
- Implement aops->set_page_dirty() in order to take control of buffer
dirtying. Not having it means if page_has_buffers(), all buffers
will be dirtied with the page. And if not they won't be. That is
fine for the moment but will break once we enable metadata updates.
For now just always using __set_page_dirty_nobuffers() for metadata
pages as nothing can dirty a page other than ourselves. Should this
change, we will really need to roll our own ->set_page_dirty().
- Implement sops->dirty_inode() to implement {a,m,c}time updates and
such things. This should probably just flag the ntfs inode such that
sops->write_inode(), i.e. ntfs_write_inode(), will copy the times
......@@ -35,7 +26,7 @@ ToDo/Notes:
- Enable the code for setting the NT4 compatibility flag when we start
making NTFS 1.2 specific modifications.
2.1.15 - .
2.1.15 - Invalidate quotas when (re)mounting read-write.
- Add new element itype.index.collation_rule to the ntfs inode
structure and set it appropriately in ntfs_read_locked_inode().
......@@ -89,6 +80,12 @@ ToDo/Notes:
dirty index records rather than having to write all the records in
the page. Modify fs/ntfs/index.h::ntfs_index_entry_mark_dirty() to
use this rather than __set_page_dirty_nobuffers().
- Implement fs/ntfs/aops.c::ntfs_write_mst_block() which enables the
writing of page cache pages belonging to mst protected attributes
like the index allocation attribute in directory indices and other
indices like $Quota/$Q, etc. This means that the quota is now marked
out of date on all volumes rather than only on ones where the quota
defaults entry is in the index root attribute of the $Quota/$Q index.
2.1.14 - Fix an NFSd caused deadlock reported by several users.
......
......@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \
index.o inode.o mft.o mst.o namei.o super.o sysctl.o unistr.o \
upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.15-WIP\"
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.15\"
ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
......
......@@ -773,6 +773,10 @@ static int ntfs_write_block(struct writeback_control *wbc, struct page *page)
return err;
}
static const char *ntfs_please_email = "Please email "
"linux-ntfs-dev@lists.sourceforge.net and say that you saw "
"this message. Thank you.";
/**
* ntfs_write_mst_block - write a @page to the backing store
* @wbc: writeback control structure
......@@ -796,13 +800,20 @@ static int ntfs_write_block(struct writeback_control *wbc, struct page *page)
static int ntfs_write_mst_block(struct writeback_control *wbc,
struct page *page)
{
struct inode *vi;
ntfs_inode *ni;
ntfs_volume *vol;
sector_t block, dblock, rec_block;;
struct inode *vi = page->mapping->host;
ntfs_inode *ni = NTFS_I(vi);
ntfs_volume *vol = ni->vol;
u8 *kaddr;
unsigned int bh_size = 1 << vi->i_blkbits;
unsigned int rec_size;
struct buffer_head *bh, *head;
int max_bhs = PAGE_CACHE_SIZE / bh_size;
struct buffer_head *bhs[max_bhs];
int i, nr_recs, nr_bhs, bhs_per_rec, err;
unsigned char bh_size_bits;
BOOL rec_is_dirty;
vi = page->mapping->host;
ni = NTFS_I(vi);
vol = ni->vol;
ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
"0x%lx.", vi->i_ino, ni->type, page->index);
BUG_ON(!NInoNonResident(ni));
......@@ -810,12 +821,186 @@ static int ntfs_write_mst_block(struct writeback_control *wbc,
BUG_ON(!(S_ISDIR(vi->i_mode) ||
(NInoAttr(ni) && ni->type == AT_INDEX_ALLOCATION)));
BUG_ON(PageWriteback(page));
BUG_ON(!PageUptodate(page));
BUG_ON(!max_bhs);
/* Make sure we have mapped buffers. */
if (unlikely(!page_has_buffers(page))) {
no_buffers_err_out:
ntfs_error(vol->sb, "Writing ntfs records without existing "
"buffers is not implemented yet. %s",
ntfs_please_email);
err = -EOPNOTSUPP;
goto err_out;
}
bh = head = page_buffers(page);
if (unlikely(!bh))
goto no_buffers_err_out;
bh_size_bits = vi->i_blkbits;
rec_size = ni->itype.index.block_size;
nr_recs = PAGE_CACHE_SIZE / rec_size;
BUG_ON(!nr_recs);
bhs_per_rec = rec_size >> bh_size_bits;
BUG_ON(!bhs_per_rec);
/* The first block in the page. */
rec_block = block = (s64)page->index <<
(PAGE_CACHE_SHIFT - bh_size_bits);
/* The first out of bounds block for the data size. */
dblock = (vi->i_size + bh_size - 1) >> bh_size_bits;
err = nr_bhs = 0;
/* Need this to silence a stupid gcc warning. */
rec_is_dirty = FALSE;
do {
if (unlikely(block >= dblock)) {
/*
* Mapped buffers outside i_size will occur, because
* this page can be outside i_size when there is a
* truncate in progress. The contents of such buffers
* were zeroed by ntfs_writepage().
*
* FIXME: What about the small race window where
* ntfs_writepage() has not done any clearing because
* the page was within i_size but before we get here,
* vmtruncate() modifies i_size?
*/
clear_buffer_dirty(bh);
continue;
}
if (rec_block == block) {
/* This block is the first one in the record. */
rec_block += rec_size >> bh_size_bits;
if (!buffer_dirty(bh)) {
/* Clean buffers are not written out. */
rec_is_dirty = FALSE;
continue;
}
rec_is_dirty = TRUE;
} else {
/* This block is not the first one in the record. */
if (!buffer_dirty(bh)) {
/* Clean buffers are not written out. */
BUG_ON(rec_is_dirty);
continue;
}
BUG_ON(!rec_is_dirty);
}
/* Attempting to write outside the initialized size is a bug. */
BUG_ON(((block + 1) << bh_size_bits) > ni->initialized_size);
if (!buffer_mapped(bh)) {
ntfs_error(vol->sb, "Writing ntfs records without "
"existing mapped buffers is not "
"implemented yet. %s",
ntfs_please_email);
clear_buffer_dirty(bh);
err = -EOPNOTSUPP;
goto cleanup_out;
}
if (!buffer_uptodate(bh)) {
ntfs_error(vol->sb, "Writing ntfs records without "
"existing uptodate buffers is not "
"implemented yet. %s",
ntfs_please_email);
clear_buffer_dirty(bh);
err = -EOPNOTSUPP;
goto cleanup_out;
}
bhs[nr_bhs++] = bh;
BUG_ON(nr_bhs > max_bhs);
} while (block++, (bh = bh->b_this_page) != head);
/* If there were no dirty buffers, we are done. */
if (!nr_bhs)
goto done;
/* Apply the mst protection fixups. */
kaddr = page_address(page);
for (i = 0; i < nr_bhs; i++) {
if (!(i % bhs_per_rec)) {
err = pre_write_mst_fixup((NTFS_RECORD*)(kaddr +
bh_offset(bhs[i])), rec_size);
if (err) {
ntfs_error(vol->sb, "Failed to apply mst "
"fixups (inode 0x%lx, "
"attribute type 0x%x, page "
"index 0x%lx)! Umount and "
"run chkdsk.", vi->i_ino,
ni->type,
page->index);
nr_bhs = i;
goto mst_cleanup_out;
}
}
}
flush_dcache_page(page);
/* Lock buffers and start synchronous write i/o on them. */
for (i = 0; i < nr_bhs; i++) {
struct buffer_head *tbh = bhs[i];
if (unlikely(test_set_buffer_locked(tbh)))
BUG();
if (unlikely(!test_clear_buffer_dirty(tbh))) {
unlock_buffer(tbh);
continue;
}
BUG_ON(!buffer_uptodate(tbh));
BUG_ON(!buffer_mapped(tbh));
get_bh(tbh);
tbh->b_end_io = end_buffer_write_sync;
submit_bh(WRITE, tbh);
}
/* Wait on i/o completion of buffers. */
for (i = 0; i < nr_bhs; i++) {
struct buffer_head *tbh = bhs[i];
wait_on_buffer(tbh);
if (unlikely(!buffer_uptodate(tbh))) {
err = -EIO;
/*
* Set the buffer uptodate so the page & buffer states
* don't become out of sync.
*/
if (PageUptodate(page))
set_buffer_uptodate(tbh);
}
}
/* Remove the mst protection fixups again. */
for (i = 0; i < nr_bhs; i++) {
if (!(i % bhs_per_rec))
post_write_mst_fixup((NTFS_RECORD*)(kaddr +
bh_offset(bhs[i])));
}
flush_dcache_page(page);
if (unlikely(err)) {
/* I/O error during writing. This is really bad! */
ntfs_error(vol->sb, "I/O error while writing ntfs record "
"(inode 0x%lx, attribute type 0x%x, page "
"index 0x%lx)! Umount and run chkdsk.",
vi->i_ino, ni->type, page->index);
goto err_out;
}
done:
set_page_writeback(page);
unlock_page(page);
end_page_writeback(page);
ntfs_warning(vi->i_sb, "Writing to index allocation attribute is not "
"supported yet. Discarding written data.");
return 0;
if (!err)
ntfs_debug("Done.");
return err;
mst_cleanup_out:
/* Remove the mst protection fixups again. */
for (i = 0; i < nr_bhs; i++) {
if (!(i % bhs_per_rec))
post_write_mst_fixup((NTFS_RECORD*)(kaddr +
bh_offset(bhs[i])));
}
cleanup_out:
/* Clean the buffers. */
for (i = 0; i < nr_bhs; i++)
clear_buffer_dirty(bhs[i]);
err_out:
SetPageError(page);
goto done;
}
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment