blob: 23f02935968809d68ef8c9eb93976b07a6ac9fdb [file] [log] [blame]
Chris Mason6cbd5572007-06-12 09:07:21 -04001/*
2 * Copyright (C) 2007 Oracle. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
17 */
18
Chris Mason39279cc2007-06-12 06:35:45 -040019#include <linux/module.h>
20#include <linux/buffer_head.h>
21#include <linux/fs.h>
22#include <linux/pagemap.h>
23#include <linux/highmem.h>
24#include <linux/time.h>
25#include <linux/init.h>
26#include <linux/string.h>
27#include <linux/smp_lock.h>
28#include <linux/backing-dev.h>
29#include <linux/mpage.h>
30#include <linux/swap.h>
31#include <linux/writeback.h>
32#include <linux/statfs.h>
33#include <linux/compat.h>
34#include "ctree.h"
35#include "disk-io.h"
36#include "transaction.h"
37#include "btrfs_inode.h"
38#include "ioctl.h"
39#include "print-tree.h"
40
41
42static int btrfs_copy_from_user(loff_t pos, int num_pages, int write_bytes,
43 struct page **prepared_pages,
44 const char __user * buf)
45{
46 long page_fault = 0;
47 int i;
48 int offset = pos & (PAGE_CACHE_SIZE - 1);
49
50 for (i = 0; i < num_pages && write_bytes > 0; i++, offset = 0) {
51 size_t count = min_t(size_t,
52 PAGE_CACHE_SIZE - offset, write_bytes);
53 struct page *page = prepared_pages[i];
54 fault_in_pages_readable(buf, count);
55
56 /* Copy data from userspace to the current page */
57 kmap(page);
58 page_fault = __copy_from_user(page_address(page) + offset,
59 buf, count);
60 /* Flush processor's dcache for this page */
61 flush_dcache_page(page);
62 kunmap(page);
63 buf += count;
64 write_bytes -= count;
65
66 if (page_fault)
67 break;
68 }
69 return page_fault ? -EFAULT : 0;
70}
71
72static void btrfs_drop_pages(struct page **pages, size_t num_pages)
73{
74 size_t i;
75 for (i = 0; i < num_pages; i++) {
76 if (!pages[i])
77 break;
78 unlock_page(pages[i]);
79 mark_page_accessed(pages[i]);
80 page_cache_release(pages[i]);
81 }
82}
83
84static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
85 struct btrfs_root *root,
86 struct file *file,
87 struct page **pages,
88 size_t num_pages,
89 loff_t pos,
90 size_t write_bytes)
91{
92 int i;
93 int offset;
94 int err = 0;
95 int ret;
96 int this_write;
97 struct inode *inode = file->f_path.dentry->d_inode;
98 struct buffer_head *bh;
99 struct btrfs_file_extent_item *ei;
100
101 for (i = 0; i < num_pages; i++) {
102 offset = pos & (PAGE_CACHE_SIZE -1);
Chris Mason84f54cf2007-06-12 07:43:08 -0400103 this_write = min((size_t)PAGE_CACHE_SIZE - offset, write_bytes);
Chris Mason39279cc2007-06-12 06:35:45 -0400104 /* FIXME, one block at a time */
105
Chris Mason39279cc2007-06-12 06:35:45 -0400106 bh = page_buffers(pages[i]);
107
108 if (buffer_mapped(bh) && bh->b_blocknr == 0) {
109 struct btrfs_key key;
110 struct btrfs_path *path;
Chris Mason34088782007-06-12 11:36:58 -0400111 char *ptr, *kaddr;
Chris Mason39279cc2007-06-12 06:35:45 -0400112 u32 datasize;
113
Chris Mason9ebefb182007-06-15 13:50:00 -0400114 mutex_lock(&root->fs_info->fs_mutex);
115 trans = btrfs_start_transaction(root, 1);
116 btrfs_set_trans_block_group(trans, inode);
117
Chris Mason39279cc2007-06-12 06:35:45 -0400118 /* create an inline extent, and copy the data in */
119 path = btrfs_alloc_path();
120 BUG_ON(!path);
121 key.objectid = inode->i_ino;
122 key.offset = pages[i]->index << PAGE_CACHE_SHIFT;
123 key.flags = 0;
124 btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
125 BUG_ON(write_bytes >= PAGE_CACHE_SIZE);
126 datasize = offset +
127 btrfs_file_extent_calc_inline_size(write_bytes);
128
129 ret = btrfs_insert_empty_item(trans, root, path, &key,
130 datasize);
131 BUG_ON(ret);
132 ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]),
133 path->slots[0], struct btrfs_file_extent_item);
134 btrfs_set_file_extent_generation(ei, trans->transid);
135 btrfs_set_file_extent_type(ei,
136 BTRFS_FILE_EXTENT_INLINE);
137 ptr = btrfs_file_extent_inline_start(ei);
Chris Mason9ebefb182007-06-15 13:50:00 -0400138
Chris Mason34088782007-06-12 11:36:58 -0400139 kaddr = kmap_atomic(bh->b_page, KM_USER0);
Chris Mason39279cc2007-06-12 06:35:45 -0400140 btrfs_memcpy(root, path->nodes[0]->b_data,
Chris Mason34088782007-06-12 11:36:58 -0400141 ptr, kaddr + bh_offset(bh),
142 offset + write_bytes);
143 kunmap_atomic(kaddr, KM_USER0);
Chris Mason9ebefb182007-06-15 13:50:00 -0400144
Chris Mason39279cc2007-06-12 06:35:45 -0400145 mark_buffer_dirty(path->nodes[0]);
146 btrfs_free_path(path);
Chris Mason9ebefb182007-06-15 13:50:00 -0400147 ret = btrfs_end_transaction(trans, root);
148 BUG_ON(ret);
149 mutex_unlock(&root->fs_info->fs_mutex);
Chris Mason39279cc2007-06-12 06:35:45 -0400150 }
Chris Mason39279cc2007-06-12 06:35:45 -0400151
152 ret = btrfs_commit_write(file, pages[i], offset,
153 offset + this_write);
154 pos += this_write;
155 if (ret) {
156 err = ret;
157 goto failed;
158 }
159 WARN_ON(this_write > write_bytes);
160 write_bytes -= this_write;
161 }
162failed:
163 return err;
164}
165
166/*
167 * this is very complex, but the basic idea is to drop all extents
168 * in the range start - end. hint_block is filled in with a block number
169 * that would be a good hint to the block allocator for this file.
170 *
171 * If an extent intersects the range but is not entirely inside the range
172 * it is either truncated or split. Anything entirely inside the range
173 * is deleted from the tree.
174 */
175int btrfs_drop_extents(struct btrfs_trans_handle *trans,
176 struct btrfs_root *root, struct inode *inode,
177 u64 start, u64 end, u64 *hint_block)
178{
179 int ret;
180 struct btrfs_key key;
181 struct btrfs_leaf *leaf;
182 int slot;
183 struct btrfs_file_extent_item *extent;
184 u64 extent_end = 0;
185 int keep;
186 struct btrfs_file_extent_item old;
187 struct btrfs_path *path;
188 u64 search_start = start;
189 int bookend;
190 int found_type;
191 int found_extent;
192 int found_inline;
193
194 path = btrfs_alloc_path();
195 if (!path)
196 return -ENOMEM;
197 while(1) {
198 btrfs_release_path(root, path);
199 ret = btrfs_lookup_file_extent(trans, root, path, inode->i_ino,
200 search_start, -1);
201 if (ret < 0)
202 goto out;
203 if (ret > 0) {
204 if (path->slots[0] == 0) {
205 ret = 0;
206 goto out;
207 }
208 path->slots[0]--;
209 }
Chris Mason8c2383c2007-06-18 09:57:58 -0400210next_slot:
Chris Mason39279cc2007-06-12 06:35:45 -0400211 keep = 0;
212 bookend = 0;
213 found_extent = 0;
214 found_inline = 0;
215 extent = NULL;
216 leaf = btrfs_buffer_leaf(path->nodes[0]);
217 slot = path->slots[0];
Chris Mason8c2383c2007-06-18 09:57:58 -0400218 ret = 0;
Chris Mason39279cc2007-06-12 06:35:45 -0400219 btrfs_disk_key_to_cpu(&key, &leaf->items[slot].key);
220 if (key.offset >= end || key.objectid != inode->i_ino) {
Chris Mason39279cc2007-06-12 06:35:45 -0400221 goto out;
222 }
Chris Mason8c2383c2007-06-18 09:57:58 -0400223 if (btrfs_key_type(&key) > BTRFS_EXTENT_DATA_KEY) {
Chris Mason39279cc2007-06-12 06:35:45 -0400224 goto out;
225 }
Chris Mason8c2383c2007-06-18 09:57:58 -0400226 if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY) {
227 extent = btrfs_item_ptr(leaf, slot,
228 struct btrfs_file_extent_item);
229 found_type = btrfs_file_extent_type(extent);
230 if (found_type == BTRFS_FILE_EXTENT_REG) {
231 extent_end = key.offset +
232 (btrfs_file_extent_num_blocks(extent) <<
233 inode->i_blkbits);
234 found_extent = 1;
235 } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
236 found_inline = 1;
237 extent_end = key.offset +
238 btrfs_file_extent_inline_len(leaf->items +
239 slot);
240 }
241 } else {
242 extent_end = search_start;
Chris Mason39279cc2007-06-12 06:35:45 -0400243 }
244
245 /* we found nothing we can drop */
Chris Mason8c2383c2007-06-18 09:57:58 -0400246 if ((!found_extent && !found_inline) ||
247 search_start >= extent_end) {
248 int nextret;
249 u32 nritems;
250 nritems = btrfs_header_nritems(
251 btrfs_buffer_header(path->nodes[0]));
252 if (slot >= nritems - 1) {
253 nextret = btrfs_next_leaf(root, path);
254 if (nextret)
255 goto out;
256 } else {
257 path->slots[0]++;
258 }
259 goto next_slot;
Chris Mason39279cc2007-06-12 06:35:45 -0400260 }
261
262 /* FIXME, there's only one inline extent allowed right now */
263 if (found_inline) {
264 u64 mask = root->blocksize - 1;
265 search_start = (extent_end + mask) & ~mask;
266 } else
267 search_start = extent_end;
268
269 if (end < extent_end && end >= key.offset) {
270 if (found_extent) {
271 u64 disk_blocknr =
272 btrfs_file_extent_disk_blocknr(extent);
273 u64 disk_num_blocks =
274 btrfs_file_extent_disk_num_blocks(extent);
275 memcpy(&old, extent, sizeof(old));
276 if (disk_blocknr != 0) {
277 ret = btrfs_inc_extent_ref(trans, root,
278 disk_blocknr, disk_num_blocks);
279 BUG_ON(ret);
280 }
281 }
282 WARN_ON(found_inline);
283 bookend = 1;
284 }
Chris Mason39279cc2007-06-12 06:35:45 -0400285 /* truncate existing extent */
286 if (start > key.offset) {
287 u64 new_num;
288 u64 old_num;
289 keep = 1;
290 WARN_ON(start & (root->blocksize - 1));
291 if (found_extent) {
292 new_num = (start - key.offset) >>
293 inode->i_blkbits;
294 old_num = btrfs_file_extent_num_blocks(extent);
295 *hint_block =
296 btrfs_file_extent_disk_blocknr(extent);
297 if (btrfs_file_extent_disk_blocknr(extent)) {
298 inode->i_blocks -=
299 (old_num - new_num) << 3;
300 }
301 btrfs_set_file_extent_num_blocks(extent,
302 new_num);
303 mark_buffer_dirty(path->nodes[0]);
304 } else {
305 WARN_ON(1);
306 }
307 }
308 /* delete the entire extent */
309 if (!keep) {
310 u64 disk_blocknr = 0;
311 u64 disk_num_blocks = 0;
312 u64 extent_num_blocks = 0;
313 if (found_extent) {
314 disk_blocknr =
315 btrfs_file_extent_disk_blocknr(extent);
316 disk_num_blocks =
317 btrfs_file_extent_disk_num_blocks(extent);
318 extent_num_blocks =
319 btrfs_file_extent_num_blocks(extent);
320 *hint_block =
321 btrfs_file_extent_disk_blocknr(extent);
322 }
323 ret = btrfs_del_item(trans, root, path);
324 BUG_ON(ret);
325 btrfs_release_path(root, path);
326 extent = NULL;
327 if (found_extent && disk_blocknr != 0) {
328 inode->i_blocks -= extent_num_blocks << 3;
329 ret = btrfs_free_extent(trans, root,
330 disk_blocknr,
331 disk_num_blocks, 0);
332 }
333
334 BUG_ON(ret);
335 if (!bookend && search_start >= end) {
336 ret = 0;
337 goto out;
338 }
339 if (!bookend)
340 continue;
341 }
342 /* create bookend, splitting the extent in two */
343 if (bookend && found_extent) {
344 struct btrfs_key ins;
345 ins.objectid = inode->i_ino;
346 ins.offset = end;
347 ins.flags = 0;
348 btrfs_set_key_type(&ins, BTRFS_EXTENT_DATA_KEY);
Chris Mason39279cc2007-06-12 06:35:45 -0400349 btrfs_release_path(root, path);
350 ret = btrfs_insert_empty_item(trans, root, path, &ins,
351 sizeof(*extent));
Chris Mason8c2383c2007-06-18 09:57:58 -0400352
353 if (ret) {
354 btrfs_print_leaf(root, btrfs_buffer_leaf(path->nodes[0]));
355 printk("got %d on inserting %Lu %u %Lu start %Lu end %Lu found %Lu %Lu\n", ret , ins.objectid, ins.flags, ins.offset, start, end, key.offset, extent_end);
356 }
Chris Mason39279cc2007-06-12 06:35:45 -0400357 BUG_ON(ret);
358 extent = btrfs_item_ptr(
359 btrfs_buffer_leaf(path->nodes[0]),
360 path->slots[0],
361 struct btrfs_file_extent_item);
362 btrfs_set_file_extent_disk_blocknr(extent,
363 btrfs_file_extent_disk_blocknr(&old));
364 btrfs_set_file_extent_disk_num_blocks(extent,
365 btrfs_file_extent_disk_num_blocks(&old));
366
367 btrfs_set_file_extent_offset(extent,
368 btrfs_file_extent_offset(&old) +
369 ((end - key.offset) >> inode->i_blkbits));
370 WARN_ON(btrfs_file_extent_num_blocks(&old) <
371 (extent_end - end) >> inode->i_blkbits);
372 btrfs_set_file_extent_num_blocks(extent,
373 (extent_end - end) >> inode->i_blkbits);
374
375 btrfs_set_file_extent_type(extent,
376 BTRFS_FILE_EXTENT_REG);
377 btrfs_set_file_extent_generation(extent,
378 btrfs_file_extent_generation(&old));
379 btrfs_mark_buffer_dirty(path->nodes[0]);
380 if (btrfs_file_extent_disk_blocknr(&old) != 0) {
381 inode->i_blocks +=
382 btrfs_file_extent_num_blocks(extent) << 3;
383 }
384 ret = 0;
385 goto out;
386 }
387 }
388out:
389 btrfs_free_path(path);
390 return ret;
391}
392
393/*
394 * this gets pages into the page cache and locks them down
395 */
396static int prepare_pages(struct btrfs_root *root,
397 struct file *file,
398 struct page **pages,
399 size_t num_pages,
400 loff_t pos,
401 unsigned long first_index,
402 unsigned long last_index,
Chris Mason8c2383c2007-06-18 09:57:58 -0400403 size_t write_bytes)
Chris Mason39279cc2007-06-12 06:35:45 -0400404{
405 int i;
406 unsigned long index = pos >> PAGE_CACHE_SHIFT;
407 struct inode *inode = file->f_path.dentry->d_inode;
408 int offset;
409 int err = 0;
410 int this_write;
411 struct buffer_head *bh;
412 struct buffer_head *head;
413 loff_t isize = i_size_read(inode);
Chris Mason8c2383c2007-06-18 09:57:58 -0400414 struct btrfs_trans_handle *trans;
415 u64 hint_block;
416 u64 num_blocks;
417 u64 alloc_extent_start;
418 u64 start_pos;
419 struct btrfs_key ins;
420
421 start_pos = pos & ~((u64)PAGE_CACHE_SIZE - 1);
422 num_blocks = (write_bytes + pos - start_pos + root->blocksize - 1) >>
423 inode->i_blkbits;
Chris Mason39279cc2007-06-12 06:35:45 -0400424
425 memset(pages, 0, num_pages * sizeof(struct page *));
426
427 for (i = 0; i < num_pages; i++) {
428 pages[i] = grab_cache_page(inode->i_mapping, index + i);
429 if (!pages[i]) {
430 err = -ENOMEM;
431 goto failed_release;
432 }
Chris Mason8c2383c2007-06-18 09:57:58 -0400433 }
434
435 mutex_lock(&root->fs_info->fs_mutex);
436 trans = btrfs_start_transaction(root, 1);
437 if (!trans) {
438 err = -ENOMEM;
439 mutex_unlock(&root->fs_info->fs_mutex);
440 goto out_unlock;
441 }
442 btrfs_set_trans_block_group(trans, inode);
443 /* FIXME blocksize != 4096 */
444 inode->i_blocks += num_blocks << 3;
445 hint_block = 0;
446
447 /* FIXME...EIEIO, ENOSPC and more */
448
449 /* step one, delete the existing extents in this range */
450 /* FIXME blocksize != pagesize */
451 if (start_pos < inode->i_size) {
452 err = btrfs_drop_extents(trans, root, inode,
453 start_pos, (pos + write_bytes + root->blocksize -1) &
454 ~((u64)root->blocksize - 1), &hint_block);
455 BUG_ON(err);
456 }
457
458 /* insert any holes we need to create */
459 if (inode->i_size < start_pos) {
460 u64 last_pos_in_file;
461 u64 hole_size;
462 u64 mask = root->blocksize - 1;
463 last_pos_in_file = (isize + mask) & ~mask;
464 hole_size = (start_pos - last_pos_in_file + mask) & ~mask;
465 hole_size >>= inode->i_blkbits;
466 if (last_pos_in_file < start_pos) {
467 err = btrfs_insert_file_extent(trans, root,
468 inode->i_ino,
469 last_pos_in_file,
470 0, 0, hole_size);
471 }
472 BUG_ON(err);
473 }
474
475 /*
476 * either allocate an extent for the new bytes or setup the key
477 * to show we are doing inline data in the extent
478 */
479 if (isize >= PAGE_CACHE_SIZE || pos + write_bytes < inode->i_size ||
480 pos + write_bytes - start_pos > BTRFS_MAX_INLINE_DATA_SIZE(root)) {
481 err = btrfs_alloc_extent(trans, root, inode->i_ino,
482 num_blocks, hint_block, (u64)-1,
483 &ins, 1);
484 BUG_ON(err);
485 err = btrfs_insert_file_extent(trans, root, inode->i_ino,
486 start_pos, ins.objectid, ins.offset,
487 ins.offset);
488 BUG_ON(err);
489 } else {
490 ins.offset = 0;
491 ins.objectid = 0;
492 }
493 BUG_ON(err);
494 alloc_extent_start = ins.objectid;
495 err = btrfs_end_transaction(trans, root);
496 mutex_unlock(&root->fs_info->fs_mutex);
497
498 for (i = 0; i < num_pages; i++) {
Chris Mason39279cc2007-06-12 06:35:45 -0400499 cancel_dirty_page(pages[i], PAGE_CACHE_SIZE);
500 wait_on_page_writeback(pages[i]);
501 offset = pos & (PAGE_CACHE_SIZE -1);
Chris Mason84f54cf2007-06-12 07:43:08 -0400502 this_write = min((size_t)PAGE_CACHE_SIZE - offset, write_bytes);
Chris Mason39279cc2007-06-12 06:35:45 -0400503 if (!page_has_buffers(pages[i])) {
504 create_empty_buffers(pages[i],
505 root->fs_info->sb->s_blocksize,
506 (1 << BH_Uptodate));
507 }
508 head = page_buffers(pages[i]);
509 bh = head;
510 do {
511 err = btrfs_map_bh_to_logical(root, bh,
512 alloc_extent_start);
513 BUG_ON(err);
514 if (err)
515 goto failed_truncate;
516 bh = bh->b_this_page;
517 if (alloc_extent_start)
518 alloc_extent_start++;
519 } while (bh != head);
520 pos += this_write;
521 WARN_ON(this_write > write_bytes);
522 write_bytes -= this_write;
523 }
524 return 0;
525
526failed_release:
527 btrfs_drop_pages(pages, num_pages);
528 return err;
529
530failed_truncate:
531 btrfs_drop_pages(pages, num_pages);
532 if (pos > isize)
533 vmtruncate(inode, isize);
534 return err;
Chris Mason8c2383c2007-06-18 09:57:58 -0400535
536out_unlock:
537 mutex_unlock(&root->fs_info->fs_mutex);
538 goto failed_release;
539
Chris Mason39279cc2007-06-12 06:35:45 -0400540}
541
542static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
543 size_t count, loff_t *ppos)
544{
545 loff_t pos;
546 size_t num_written = 0;
547 int err = 0;
548 int ret = 0;
549 struct inode *inode = file->f_path.dentry->d_inode;
550 struct btrfs_root *root = BTRFS_I(inode)->root;
Chris Mason8c2383c2007-06-18 09:57:58 -0400551 struct page **pages = NULL;
552 int nrptrs;
Chris Mason39279cc2007-06-12 06:35:45 -0400553 struct page *pinned[2];
554 unsigned long first_index;
555 unsigned long last_index;
Chris Mason8c2383c2007-06-18 09:57:58 -0400556
557 nrptrs = min((count + PAGE_CACHE_SIZE - 1) / PAGE_CACHE_SIZE,
558 PAGE_CACHE_SIZE / (sizeof(struct page *)));
Chris Mason39279cc2007-06-12 06:35:45 -0400559 pinned[0] = NULL;
560 pinned[1] = NULL;
561 if (file->f_flags & O_DIRECT)
562 return -EINVAL;
563 pos = *ppos;
564 vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
565 current->backing_dev_info = inode->i_mapping->backing_dev_info;
566 err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
567 if (err)
568 goto out;
569 if (count == 0)
570 goto out;
571 err = remove_suid(file->f_path.dentry);
572 if (err)
573 goto out;
574 file_update_time(file);
575
Chris Mason8c2383c2007-06-18 09:57:58 -0400576 pages = kmalloc(nrptrs * sizeof(struct page *), GFP_KERNEL);
Chris Mason39279cc2007-06-12 06:35:45 -0400577
578 mutex_lock(&inode->i_mutex);
579 first_index = pos >> PAGE_CACHE_SHIFT;
580 last_index = (pos + count) >> PAGE_CACHE_SHIFT;
581
582 /*
583 * there are lots of better ways to do this, but this code
584 * makes sure the first and last page in the file range are
585 * up to date and ready for cow
586 */
587 if ((pos & (PAGE_CACHE_SIZE - 1))) {
588 pinned[0] = grab_cache_page(inode->i_mapping, first_index);
589 if (!PageUptodate(pinned[0])) {
Chris Mason9ebefb182007-06-15 13:50:00 -0400590 ret = btrfs_readpage(NULL, pinned[0]);
Chris Mason39279cc2007-06-12 06:35:45 -0400591 BUG_ON(ret);
592 wait_on_page_locked(pinned[0]);
593 } else {
594 unlock_page(pinned[0]);
595 }
596 }
597 if ((pos + count) & (PAGE_CACHE_SIZE - 1)) {
598 pinned[1] = grab_cache_page(inode->i_mapping, last_index);
599 if (!PageUptodate(pinned[1])) {
Chris Mason9ebefb182007-06-15 13:50:00 -0400600 ret = btrfs_readpage(NULL, pinned[1]);
Chris Mason39279cc2007-06-12 06:35:45 -0400601 BUG_ON(ret);
602 wait_on_page_locked(pinned[1]);
603 } else {
604 unlock_page(pinned[1]);
605 }
606 }
607
Chris Mason39279cc2007-06-12 06:35:45 -0400608 while(count > 0) {
609 size_t offset = pos & (PAGE_CACHE_SIZE - 1);
Chris Mason11bd1432007-06-22 14:16:24 -0400610 size_t write_bytes = min(count, nrptrs *
611 (size_t)PAGE_CACHE_SIZE -
Chris Mason8c2383c2007-06-18 09:57:58 -0400612 offset);
Chris Mason39279cc2007-06-12 06:35:45 -0400613 size_t num_pages = (write_bytes + PAGE_CACHE_SIZE - 1) >>
614 PAGE_CACHE_SHIFT;
615
Chris Mason8c2383c2007-06-18 09:57:58 -0400616 WARN_ON(num_pages > nrptrs);
Chris Mason39279cc2007-06-12 06:35:45 -0400617 memset(pages, 0, sizeof(pages));
618 ret = prepare_pages(root, file, pages, num_pages,
619 pos, first_index, last_index,
Chris Mason8c2383c2007-06-18 09:57:58 -0400620 write_bytes);
Chris Mason39279cc2007-06-12 06:35:45 -0400621 BUG_ON(ret);
622
Chris Mason39279cc2007-06-12 06:35:45 -0400623 ret = btrfs_copy_from_user(pos, num_pages,
624 write_bytes, pages, buf);
625 BUG_ON(ret);
626
627 ret = dirty_and_release_pages(NULL, root, file, pages,
628 num_pages, pos, write_bytes);
629 BUG_ON(ret);
630 btrfs_drop_pages(pages, num_pages);
631
632 buf += write_bytes;
633 count -= write_bytes;
634 pos += write_bytes;
635 num_written += write_bytes;
636
Chris Mason8c2383c2007-06-18 09:57:58 -0400637 balance_dirty_pages_ratelimited_nr(inode->i_mapping, num_pages);
Chris Mason39279cc2007-06-12 06:35:45 -0400638 btrfs_btree_balance_dirty(root);
639 cond_resched();
640 }
Chris Mason39279cc2007-06-12 06:35:45 -0400641 mutex_unlock(&inode->i_mutex);
642out:
Chris Mason8c2383c2007-06-18 09:57:58 -0400643 kfree(pages);
Chris Mason39279cc2007-06-12 06:35:45 -0400644 if (pinned[0])
645 page_cache_release(pinned[0]);
646 if (pinned[1])
647 page_cache_release(pinned[1]);
648 *ppos = pos;
649 current->backing_dev_info = NULL;
650 mark_inode_dirty(inode);
651 return num_written ? num_written : err;
652}
653
Chris Mason39279cc2007-06-12 06:35:45 -0400654static int btrfs_sync_file(struct file *file,
655 struct dentry *dentry, int datasync)
656{
657 struct inode *inode = dentry->d_inode;
658 struct btrfs_root *root = BTRFS_I(inode)->root;
659 int ret;
660 struct btrfs_trans_handle *trans;
661
662 /*
663 * FIXME, use inode generation number to check if we can skip the
664 * commit
665 */
666 mutex_lock(&root->fs_info->fs_mutex);
667 trans = btrfs_start_transaction(root, 1);
668 if (!trans) {
669 ret = -ENOMEM;
670 goto out;
671 }
672 ret = btrfs_commit_transaction(trans, root);
673 mutex_unlock(&root->fs_info->fs_mutex);
674out:
675 return ret > 0 ? EIO : ret;
676}
677
Chris Mason9ebefb182007-06-15 13:50:00 -0400678static struct vm_operations_struct btrfs_file_vm_ops = {
679 .nopage = filemap_nopage,
680 .populate = filemap_populate,
681 .page_mkwrite = btrfs_page_mkwrite,
682};
683
684static int btrfs_file_mmap(struct file *filp, struct vm_area_struct *vma)
685{
686 vma->vm_ops = &btrfs_file_vm_ops;
687 file_accessed(filp);
688 return 0;
689}
690
Chris Mason39279cc2007-06-12 06:35:45 -0400691struct file_operations btrfs_file_operations = {
692 .llseek = generic_file_llseek,
693 .read = do_sync_read,
Chris Mason9ebefb182007-06-15 13:50:00 -0400694 .aio_read = generic_file_aio_read,
Chris Mason39279cc2007-06-12 06:35:45 -0400695 .write = btrfs_file_write,
Chris Mason9ebefb182007-06-15 13:50:00 -0400696 .mmap = btrfs_file_mmap,
Chris Mason39279cc2007-06-12 06:35:45 -0400697 .open = generic_file_open,
698 .ioctl = btrfs_ioctl,
699 .fsync = btrfs_sync_file,
700#ifdef CONFIG_COMPAT
701 .compat_ioctl = btrfs_compat_ioctl,
702#endif
703};
704