summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext2fs/truncate.c154
1 files changed, 93 insertions, 61 deletions
diff --git a/ext2fs/truncate.c b/ext2fs/truncate.c
index 5d652255..cfbc5695 100644
--- a/ext2fs/truncate.c
+++ b/ext2fs/truncate.c
@@ -33,18 +33,46 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-/*
- * Truncate has the most races in the whole filesystem: coding it is
- * a pain in the a**. Especially as I don't do any locking...
- *
- * The code may look a bit weird, but that's just because I've tried to
- * handle things like file-size changes in a somewhat graceful manner.
- * Anyway, truncating a file at the same time somebody else writes to it
- * is likely to result in pretty weird behaviour...
- *
- * The new code handles normal truncates (size = 0) as well as the more
- * general case (size = XXX). I hope.
- */
+#include "ext2fs.h"
+
+#ifdef DONT_CACHE_MEMORY_OBJECTS
+#define MAY_CACHE 0
+#else
+#define MAY_CACHE 1
+#endif
+
+/* ---------------------------------------------------------------- */
+
+/* Write something to each page from START to END inclusive of memory
+ object OBJ, but make sure the data doesns't actually change. */
+static void
+poke_pages (memory_object_t obj,
+ vm_offset_t start,
+ vm_offset_t end)
+{
+ vm_address_t addr, poke;
+ vm_size_t len;
+ error_t err;
+
+ while (start < end)
+ {
+ len = 8 * vm_page_size;
+ if (len > end - start)
+ len = end - start;
+ addr = 0;
+ err = vm_map (mach_task_self (), &addr, len, 0, 1, obj, start, 0,
+ VM_PROT_WRITE|VM_PROT_READ, VM_PROT_READ|VM_PROT_WRITE, 0);
+ if (!err)
+ {
+ for (poke = addr; poke < addr + len; poke += vm_page_size)
+ *(volatile int *)poke = *(volatile int *)poke;
+ vm_deallocate (mach_task_self (), addr, len);
+ }
+ start += len;
+ }
+}
+
+/* ---------------------------------------------------------------- */
#define DIRECT_BLOCK(length) \
((length + block_size - 1) / block_size)
@@ -70,16 +98,13 @@ trunc_direct (struct node * node, unsigned long length)
for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++)
{
- block = node->dn.info.i_data[i];
+ block = node->dn->info.i_data[i];
if (!block)
continue;
bh = bptr(block);
- if (i < direct_block)
- goto repeat;
-
- node->dn.info.i_data[i] = 0;
+ node->dn->info.i_data[i] = 0;
node->dn_stat.st_blocks -= blocks;
node->dn_stat_dirty = 1;
@@ -103,6 +128,8 @@ trunc_direct (struct node * node, unsigned long length)
if (free_count > 0)
ext2_free_blocks (block_to_free, free_count);
}
+
+/* ---------------------------------------------------------------- */
static void
trunc_indirect (struct node * node, unsigned long length,
@@ -119,12 +146,12 @@ trunc_indirect (struct node * node, unsigned long length,
block = *p;
if (!block)
- return 0;
+ return;
ind_bh = bptr (block);
if (!ind_bh) {
*p = 0;
- return 0;
+ return;
}
for (i = indirect_block ; i < addr_per_block ; i++)
@@ -138,11 +165,9 @@ trunc_indirect (struct node * node, unsigned long length,
continue;
bh = bptr (block);
- if (i < indirect_block)
- goto repeat;
*ind = 0;
- pokel_add (&node->dn.pokel, ind, sizeof *ind);
+ pokel_add (&node->dn->pokel, ind, sizeof *ind);
if (free_count == 0)
{
@@ -181,9 +206,9 @@ trunc_indirect (struct node * node, unsigned long length,
node->dn_stat_dirty = 1;
ext2_free_blocks (block, 1);
}
-
- return retry;
}
+
+/* ---------------------------------------------------------------- */
static void
trunc_dindirect (struct node * node, unsigned long length,
@@ -197,13 +222,13 @@ trunc_dindirect (struct node * node, unsigned long length,
block = *p;
if (!block)
- return 0;
+ return;
dind_bh = bptr (block);
if (!dind_bh)
{
*p = 0;
- return 0;
+ return;
}
for (i = dindirect_block ; i < addr_per_block ; i++) {
@@ -215,9 +240,9 @@ trunc_dindirect (struct node * node, unsigned long length,
if (!block)
continue;
- trunc_indirect (node, offset + (i * addr_per_block), dind);
+ trunc_indirect (node, length, offset + (i * addr_per_block), dind);
- pokel_add (&node->dn.pokel, dindh_bh, block_size);
+ pokel_add (&node->dn->pokel, dind_bh, block_size);
}
dind = (u32 *) dind_bh;
@@ -233,9 +258,9 @@ trunc_dindirect (struct node * node, unsigned long length,
node->dn_stat_dirty = 1;
ext2_free_blocks (block, 1);
}
-
- return retry;
}
+
+/* ---------------------------------------------------------------- */
static void
trunc_tindirect (struct node * node, unsigned long length)
@@ -243,19 +268,18 @@ trunc_tindirect (struct node * node, unsigned long length)
int i, block;
char * tind_bh;
u32 * tind, * p;
- int retry = 0;
int blocks = block_size / 512;
int tindirect_block = TINDIRECT_BLOCK (length);
- p = node->dn.info.i_data + EXT2_TIND_BLOCK;
+ p = node->dn->info.i_data + EXT2_TIND_BLOCK;
if (!(block = *p))
- return 0;
+ return;
tind_bh = bptr (block);
if (!tind_bh)
{
*p = 0;
- return 0;
+ return;
}
for (i = tindirect_block ; i < addr_per_block ; i++)
@@ -264,12 +288,12 @@ trunc_tindirect (struct node * node, unsigned long length)
i = 0;
tind = i + (u32 *) tind_bh;
- trunc_dindirect(node,
+ trunc_dindirect(node, length,
(EXT2_NDIR_BLOCKS
+ addr_per_block
+ (i + 1) * addr_per_block * addr_per_block),
tind);
- pokel_add (&node->dn.pokel, tindh_bh, block_size);
+ pokel_add (&node->dn->pokel, tind_bh, block_size);
}
tind = (u32 *) tind_bh;
@@ -296,8 +320,10 @@ trunc_tindirect (struct node * node, unsigned long length)
static void
force_delayed_copies (struct node *node, off_t length)
{
+ struct user_pager_info *upi;
+
spin_lock (&node_to_page_lock);
- upi = np->dn->fileinfo;
+ upi = node->dn->fileinfo;
if (upi)
pager_reference (upi->p);
spin_unlock (&node_to_page_lock);
@@ -307,10 +333,10 @@ force_delayed_copies (struct node *node, off_t length)
mach_port_t obj;
pager_change_attributes (upi->p, MAY_CACHE, MEMORY_OBJECT_COPY_NONE, 1);
- obj = diskfs_get_filemap (np);
- poke_pages (obj, round_page (length), round_page (np->allocsize));
+ obj = diskfs_get_filemap (node);
+ poke_pages (obj, round_page (length), round_page (node->allocsize));
mach_port_deallocate (mach_task_self (), obj);
- pager_flush_some (upi->p, round_page(length), np->allocsize - length, 1);
+ pager_flush_some (upi->p, round_page(length), node->allocsize - length, 1);
pager_unreference (upi->p);
}
}
@@ -318,10 +344,13 @@ force_delayed_copies (struct node *node, off_t length)
static void
enable_delayed_copies (struct node *node)
{
+ struct user_pager_info *upi;
+
spin_lock (&node_to_page_lock);
- upi = np->dn->fileinfo;
+ upi = node->dn->fileinfo;
if (upi)
pager_reference (upi->p);
+
spin_unlock (&node_to_page_lock);
if (upi)
{
@@ -332,16 +361,17 @@ enable_delayed_copies (struct node *node)
/* ---------------------------------------------------------------- */
-/* The user must define this function. Truncate locked node NP to be SIZE
- bytes long. (If NP is already less than or equal to SIZE bytes
+/* The user must define this function. Truncate locked node NODE to be SIZE
+ bytes long. (If NODE is already less than or equal to SIZE bytes
long, do nothing.) If this is a symlink (and diskfs_shortcut_symlink
is set) then this should clear the symlink, even if
diskfs_create_symlink_hook stores the link target elsewhere. */
error_t
-diskfs_truncate (struct node *np, off_t length)
+diskfs_truncate (struct node *node, off_t length)
{
+ error_t err;
int offset;
- mode_t mode = node->dn_state.st_mode;
+ mode_t mode = node->dn_stat.st_mode;
if (S_ISDIR(mode))
return EISDIR;
@@ -350,7 +380,7 @@ diskfs_truncate (struct node *np, off_t length)
if (IS_APPEND(node) || IS_IMMUTABLE(node))
return EINVAL;
- if (length >= np->dn_stat.st_size)
+ if (length >= node->dn_stat.st_size)
return 0;
assert (!diskfs_readonly);
@@ -364,33 +394,33 @@ diskfs_truncate (struct node *np, off_t length)
offset = length % block_size;
if (offset > 0)
{
- diskfs_node_rdwr (np, (void *)zeroblock, length, block_size - offset,
+ diskfs_node_rdwr (node, (void *)zeroblock, length, block_size - offset,
1, 0, 0);
- diskfs_file_update (np, 1);
+ diskfs_file_update (node, 1);
}
ext2_discard_prealloc(node);
- force_delayed_copies (np, length);
+ force_delayed_copies (node, length);
- rwlock_writer_lock (&np->dn->alloc_lock);
+ rwlock_writer_lock (&node->dn->alloc_lock);
/* Update the size on disk; fsck will finish freeing blocks if necessary
should we crash. */
- np->dn_stat.st_size = length;
- np->dn_set_mtime = 1;
- np->dn_set_ctime = 1;
- diskfs_node_update (np, 1);
+ node->dn_stat.st_size = length;
+ node->dn_set_mtime = 1;
+ node->dn_set_ctime = 1;
+ diskfs_node_update (node, 1);
err = diskfs_catch_exception();
if (!err)
{
trunc_direct(node, length);
trunc_indirect (node, length, EXT2_IND_BLOCK,
- (u32 *) &node->dn.info.i_data[EXT2_IND_BLOCK]);
+ (u32 *) &node->dn->info.i_data[EXT2_IND_BLOCK]);
trunc_dindirect (node, length, EXT2_IND_BLOCK +
EXT2_ADDR_PER_BLOCK(sblock),
- (u32 *) &node->dn.info.i_data[EXT2_DIND_BLOCK]);
+ (u32 *) &node->dn->info.i_data[EXT2_DIND_BLOCK]);
trunc_tindirect (node, length);
}
@@ -399,17 +429,19 @@ diskfs_truncate (struct node *np, off_t length)
node->dn_stat_dirty = 1;
/* Now we can permit delayed copies again. */
- enable_delayed_copies (np);
+ enable_delayed_copies (node);
+
+ return 0;
}
/* The user must define this function. Grow the disk allocated to locked node
- NP to be at least SIZE bytes, and set NP->allocsize to the actual
+ NODE to be at least SIZE bytes, and set NODE->allocsize to the actual
allocated size. (If the allocated size is already SIZE bytes, do
nothing.) CRED identifies the user responsible for the call. */
error_t
-diskfs_grow (struct node *np, off_t size, struct protid *cred)
+diskfs_grow (struct node *node, off_t size, struct protid *cred)
{
- if (size > np->allocsize)
- np->allocsize = size;
+ if (size > node->allocsize)
+ node->allocsize = size;
return 0;
}