Skip to content

Commit a1d75f2

Browse files
author
Miklos Szeredi
committed
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 <[email protected]>
1 parent 7909b1c commit a1d75f2

File tree

4 files changed

+103
-2
lines changed

4 files changed

+103
-2
lines changed

fs/fuse/dev.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,91 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
12311231
return err;
12321232
}
12331233

1234+
static 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+
1310+
out_iput:
1311+
iput(inode);
1312+
out_up_killsb:
1313+
up_read(&fc->killsb);
1314+
out_finish:
1315+
fuse_copy_finish(cs);
1316+
return err;
1317+
}
1318+
12341319
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
12351320
unsigned int size, struct fuse_copy_state *cs)
12361321
{
@@ -1244,6 +1329,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
12441329
case FUSE_NOTIFY_INVAL_ENTRY:
12451330
return fuse_notify_inval_entry(fc, size, cs);
12461331

1332+
case FUSE_NOTIFY_STORE:
1333+
return fuse_notify_store(fc, size, cs);
1334+
12471335
default:
12481336
fuse_copy_finish(cs);
12491337
return -EINVAL;

fs/fuse/file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping,
706706
return 0;
707707
}
708708

709-
static void fuse_write_update_size(struct inode *inode, loff_t pos)
709+
void fuse_write_update_size(struct inode *inode, loff_t pos)
710710
{
711711
struct fuse_conn *fc = get_fuse_conn(inode);
712712
struct fuse_inode *fi = get_fuse_inode(inode);

fs/fuse/fuse_i.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,4 +748,6 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
748748
unsigned fuse_file_poll(struct file *file, poll_table *wait);
749749
int fuse_dev_release(struct inode *inode, struct file *file);
750750

751+
void fuse_write_update_size(struct inode *inode, loff_t pos);
752+
751753
#endif /* _FS_FUSE_I_H */

include/linux/fuse.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
*
3838
* 7.14
3939
* - add splice support to fuse device
40+
*
41+
* 7.15
42+
* - add store notify
4043
*/
4144

4245
#ifndef _LINUX_FUSE_H
@@ -68,7 +71,7 @@
6871
#define FUSE_KERNEL_VERSION 7
6972

7073
/** Minor version number of this interface */
71-
#define FUSE_KERNEL_MINOR_VERSION 14
74+
#define FUSE_KERNEL_MINOR_VERSION 15
7275

7376
/** The node ID of the root inode */
7477
#define FUSE_ROOT_ID 1
@@ -260,6 +263,7 @@ enum fuse_notify_code {
260263
FUSE_NOTIFY_POLL = 1,
261264
FUSE_NOTIFY_INVAL_INODE = 2,
262265
FUSE_NOTIFY_INVAL_ENTRY = 3,
266+
FUSE_NOTIFY_STORE = 4,
263267
FUSE_NOTIFY_CODE_MAX,
264268
};
265269

@@ -568,4 +572,11 @@ struct fuse_notify_inval_entry_out {
568572
__u32 padding;
569573
};
570574

575+
struct fuse_notify_store_out {
576+
__u64 nodeid;
577+
__u64 offset;
578+
__u32 size;
579+
__u32 padding;
580+
};
581+
571582
#endif /* _LINUX_FUSE_H */

0 commit comments

Comments
 (0)