diff options
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r-- | fs/fuse/dev.c | 88 |
1 files changed, 88 insertions, 0 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 | ||
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 | |||
1234 | static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, | 1319 | static 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; |