aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cachefiles/interface.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-11-19 18:11:52 +0000
committerDavid Howells <dhowells@redhat.com>2009-11-19 18:11:52 +0000
commita17754fb8c28af19cd70dcbec6d5b0773b94e0c1 (patch)
treed7c25b217c684153eadbac78ab9b1bbff08b75f6 /fs/cachefiles/interface.c
parent868411be3f445a83fafbd734f3e426400138add5 (diff)
downloadkernel_samsung_smdk4412-a17754fb8c28af19cd70dcbec6d5b0773b94e0c1.zip
kernel_samsung_smdk4412-a17754fb8c28af19cd70dcbec6d5b0773b94e0c1.tar.gz
kernel_samsung_smdk4412-a17754fb8c28af19cd70dcbec6d5b0773b94e0c1.tar.bz2
CacheFiles: Don't write a full page if there's only a partial page to cache
cachefiles_write_page() writes a full page to the backing file for the last page of the netfs file, even if the netfs file's last page is only a partial page. This causes the EOF on the backing file to be extended beyond the EOF of the netfs, and thus the backing file will be truncated by cachefiles_attr_changed() called from cachefiles_lookup_object(). So we need to limit the write we make to the backing file on that last page such that it doesn't push the EOF too far. Also, if a backing file that has a partial page at the end is expanded, we discard the partial page and refetch it on the basis that we then have a hole in the file with invalid data, and should the power go out... A better way to deal with this could be to record a note that the partial page contains invalid data until the correct data is written into it. This isn't a problem for netfs's that discard the whole backing file if the file size changes (such as NFS). Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/cachefiles/interface.c')
-rw-r--r--fs/cachefiles/interface.c20
1 files changed, 17 insertions, 3 deletions
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c
index dd7f852..8e67abf 100644
--- a/fs/cachefiles/interface.c
+++ b/fs/cachefiles/interface.c
@@ -404,12 +404,26 @@ static int cachefiles_attr_changed(struct fscache_object *_object)
if (oi_size == ni_size)
return 0;
- newattrs.ia_size = ni_size;
- newattrs.ia_valid = ATTR_SIZE;
-
cachefiles_begin_secure(cache, &saved_cred);
mutex_lock(&object->backer->d_inode->i_mutex);
+
+ /* if there's an extension to a partial page at the end of the backing
+ * file, we need to discard the partial page so that we pick up new
+ * data after it */
+ if (oi_size & ~PAGE_MASK && ni_size > oi_size) {
+ _debug("discard tail %llx", oi_size);
+ newattrs.ia_valid = ATTR_SIZE;
+ newattrs.ia_size = oi_size & PAGE_MASK;
+ ret = notify_change(object->backer, &newattrs);
+ if (ret < 0)
+ goto truncate_failed;
+ }
+
+ newattrs.ia_valid = ATTR_SIZE;
+ newattrs.ia_size = ni_size;
ret = notify_change(object->backer, &newattrs);
+
+truncate_failed:
mutex_unlock(&object->backer->d_inode->i_mutex);
cachefiles_end_secure(cache, saved_cred);