aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2010-07-12 08:41:40 -0400
committerMiklos Szeredi <mszeredi@suse.cz>2010-07-12 08:41:40 -0400
commita1d75f258230b75d46aecdf28b2e732413028863 (patch)
tree2ab11fc692c8426551045b7ab9a290dfa770821c /fs/fuse
parent7909b1c64078087ac153fb47a2f50793fe3ee7d0 (diff)
fuse: add store request
Userspace filesystem can request data to be stored in the inode's mapping. This request is synchronous and has no reply. If the write to the fuse device returns an error then the store request was not fully completed (but may have updated some pages). If the stored data overflows the current file size, then the size is extended, similarly to a write(2) on the filesystem. Pages which have been completely stored are marked uptodate. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/dev.c88
-rw-r--r--fs/fuse/file.c2
-rw-r--r--fs/fuse/fuse_i.h2
3 files changed, 91 insertions, 1 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 7eb80d33c4f3..8e01c865586e 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1231,6 +1231,91 @@ err:
1231 return err; 1231 return err;
1232} 1232}
1233 1233
1234static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
1235 struct fuse_copy_state *cs)
1236{
1237 struct fuse_notify_store_out outarg;
1238 struct inode *inode;
1239 struct address_space *mapping;
1240 u64 nodeid;
1241 int err;
1242 pgoff_t index;
1243 unsigned int offset;
1244 unsigned int num;
1245 loff_t file_size;
1246 loff_t end;
1247
1248 err = -EINVAL;
1249 if (size < sizeof(outarg))
1250 goto out_finish;
1251
1252 err = fuse_copy_one(cs, &outarg, sizeof(outarg));
1253 if (err)
1254 goto out_finish;
1255
1256 err = -EINVAL;
1257 if (size - sizeof(outarg) != outarg.size)
1258 goto out_finish;
1259
1260 nodeid = outarg.nodeid;
1261
1262 down_read(&fc->killsb);
1263
1264 err = -ENOENT;
1265 if (!fc->sb)
1266 goto out_up_killsb;
1267
1268 inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
1269 if (!inode)
1270 goto out_up_killsb;
1271
1272 mapping = inode->i_mapping;
1273 index = outarg.offset >> PAGE_CACHE_SHIFT;
1274 offset = outarg.offset & ~PAGE_CACHE_MASK;
1275 file_size = i_size_read(inode);
1276 end = outarg.offset + outarg.size;
1277 if (end > file_size) {
1278 file_size = end;
1279 fuse_write_update_size(inode, file_size);
1280 }
1281
1282 num = outarg.size;
1283 while (num) {
1284 struct page *page;
1285 unsigned int this_num;
1286
1287 err = -ENOMEM;
1288 page = find_or_create_page(mapping, index,
1289 mapping_gfp_mask(mapping));
1290 if (!page)
1291 goto out_iput;
1292
1293 this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset);
1294 err = fuse_copy_page(cs, &page, offset, this_num, 0);
1295 if (!err && offset == 0 && (num != 0 || file_size == end))
1296 SetPageUptodate(page);
1297 unlock_page(page);
1298 page_cache_release(page);
1299
1300 if (err)
1301 goto out_iput;
1302
1303 num -= this_num;
1304 offset = 0;
1305 index++;
1306 }
1307
1308 err = 0;
1309
1310out_iput:
1311 iput(inode);
1312out_up_killsb:
1313 up_read(&fc->killsb);
1314out_finish:
1315 fuse_copy_finish(cs);
1316 return err;
1317}
1318
1234static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, 1319static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
1235 unsigned int size, struct fuse_copy_state *cs) 1320 unsigned int size, struct fuse_copy_state *cs)
1236{ 1321{
@@ -1244,6 +1329,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
1244 case FUSE_NOTIFY_INVAL_ENTRY: 1329 case FUSE_NOTIFY_INVAL_ENTRY:
1245 return fuse_notify_inval_entry(fc, size, cs); 1330 return fuse_notify_inval_entry(fc, size, cs);
1246 1331
1332 case FUSE_NOTIFY_STORE:
1333 return fuse_notify_store(fc, size, cs);
1334
1247 default: 1335 default:
1248 fuse_copy_finish(cs); 1336 fuse_copy_finish(cs);
1249 return -EINVAL; 1337 return -EINVAL;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index ada0adeb3bb5..147c1f71bdb9 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -706,7 +706,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping,
706 return 0; 706 return 0;
707} 707}
708 708
709static void fuse_write_update_size(struct inode *inode, loff_t pos) 709void fuse_write_update_size(struct inode *inode, loff_t pos)
710{ 710{
711 struct fuse_conn *fc = get_fuse_conn(inode); 711 struct fuse_conn *fc = get_fuse_conn(inode);
712 struct fuse_inode *fi = get_fuse_inode(inode); 712 struct fuse_inode *fi = get_fuse_inode(inode);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 8f309f04064e..61267d8d527b 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -748,4 +748,6 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
748unsigned fuse_file_poll(struct file *file, poll_table *wait); 748unsigned fuse_file_poll(struct file *file, poll_table *wait);
749int fuse_dev_release(struct inode *inode, struct file *file); 749int fuse_dev_release(struct inode *inode, struct file *file);
750 750
751void fuse_write_update_size(struct inode *inode, loff_t pos);
752
751#endif /* _FS_FUSE_I_H */ 753#endif /* _FS_FUSE_I_H */