> > In that case, why was it changed for FAT only? Ext2 will still
> > happily enlarge a file by truncating it.
>
> ftruncate() and truncate() may extend a file but they are not required to
> do so.
>
> > If the behavior has to be changed, wouldn't it be better to first
> > give people a chance to get programs, that rely on the old
> > behavior fixed, before enforcing the change?
>
> A program relying on the old behaviour was violating standards. Also its been
> this way for almost two years.
>
> > Staroffice (the binary-only version; the new "open source"
> > version is not yet ready for real-world use) for example
> > currently doesn't write to FAT filesystems anymore - which is
> > pretty annoying for people who need it.
> >
> > Is there somewhere a patch for the current kernel?
>
> You might be able to fish it out of old -ac kernel trees and debug it further.
> Alternatively you could implement it in glibc of course, which is a nicer
> solution
Alan, fix is really quite simple. Especially if you have vmtruncate()
returning int (ac1 used to do it, I didn't check later ones). Actually
just a generic_cont_expand() done on expanding path in vmtruncate()
will be enough - it should be OK for all cases, including normal
filesystems. <grabbing -ac7>
OK, any brave soul to test that? All I can promise that it builds.
Cheers,
Al
diff -urN S2-ac7/fs/affs/file.c S2-ac7-truncate/fs/affs/file.c
--- S2-ac7/fs/affs/file.c Fri Feb 16 22:52:08 2001
+++ S2-ac7-truncate/fs/affs/file.c Thu Mar 1 14:22:54 2001
@@ -663,25 +663,6 @@
net_blocksize = blocksize - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
first = inode->i_size + net_blocksize -1;
do_div (first, net_blocksize);
- if (inode->u.affs_i.i_lastblock < first - 1) {
- /* There has to be at least one new block to be allocated */
- if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode)) {
- /* XXX Fine! No way to indicate an error. */
- return /* -ENOSPC */;
- }
- bh = affs_getblock(inode,first - 1);
- if (!bh) {
- affs_warning(inode->i_sb,"truncate","Cannot extend file");
- inode->i_size = net_blocksize * (inode->u.affs_i.i_lastblock + 1);
- } else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
- tmp = inode->i_size;
- rem = do_div(tmp, net_blocksize);
- DATA_FRONT(bh)->data_size = cpu_to_be32(rem ? rem : net_blocksize);
- affs_fix_checksum(blocksize,bh->b_data,5);
- mark_buffer_dirty(bh);
- }
- goto out_truncate;
- }
ekey = inode->i_ino;
ext = 0;
diff -urN S2-ac7/fs/fat/inode.c S2-ac7-truncate/fs/fat/inode.c
--- S2-ac7/fs/fat/inode.c Thu Mar 1 14:05:17 2001
+++ S2-ac7-truncate/fs/fat/inode.c Thu Mar 1 14:21:45 2001
@@ -904,12 +904,6 @@
struct inode *inode = dentry->d_inode;
int error;
- /* FAT cannot truncate to a longer file */
- if (attr->ia_valid & ATTR_SIZE) {
- if (attr->ia_size > inode->i_size)
- return -EPERM;
- }
-
error = inode_change_ok(inode, attr);
if (error)
return MSDOS_SB(sb)->options.quiet ? 0 : error;
diff -urN S2-ac7/mm/memory.c S2-ac7-truncate/mm/memory.c
--- S2-ac7/mm/memory.c Thu Mar 1 14:05:20 2001
+++ S2-ac7-truncate/mm/memory.c Thu Mar 1 14:21:05 2001
@@ -924,7 +924,32 @@
zap_page_range(mm, start, len);
} while ((mpnt = mpnt->vm_next_share) != NULL);
}
-
+
+static int generic_vm_expand(struct address_space *mapping, loff_t size)
+{
+ struct page *page;
+ unsigned long index, offset;
+ int err;
+
+ if (!mapping->a_ops->prepare_write || !mapping->a_ops->commit_write)
+ return -ENOSYS;
+
+ offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */
+ index = size >> PAGE_CACHE_SHIFT;
+ err = -ENOMEM;
+ page = grab_cache_page(mapping, index);
+ if (!page)
+ goto out;
+ err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
+ if (!err)
+ err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+ UnlockPage(page);
+ page_cache_release(page);
+ if (err > 0)
+ err = 0;
+out:
+ return err;
+}
/*
* Handle all mappings that got truncated by a "truncate()"
@@ -939,6 +964,7 @@
unsigned long partial, pgoff;
struct address_space *mapping = inode->i_mapping;
unsigned long limit;
+ int err;
if (inode->i_size < offset)
goto do_expand;
@@ -976,10 +1002,14 @@
offset = limit;
}
}
- inode->i_size = offset;
- if (inode->i_op && inode->i_op->truncate)
+ err = generic_vm_expand(mapping, offset);
+ if (err == -ENOSYS) {
+ err = 0;
+ inode->i_size = offset;
+ }
+ if (!err && inode->i_op && inode->i_op->truncate)
inode->i_op->truncate(inode);
- return 0;
+ return err;
out:
return -EFBIG;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/