Blame view

fs/afs/file.c 8.56 KB
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
1
  /* AFS filesystem file handling
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
   *
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
3
   * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
   * Written by David Howells (dhowells@redhat.com)
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  #include <linux/fs.h>
  #include <linux/pagemap.h>
31143d5d5   David Howells   AFS: implement ba...
17
  #include <linux/writeback.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/gfp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include "internal.h"
416351f28   David Howells   AFS: AFS fixups
20
21
22
  static int afs_readpage(struct file *file, struct page *page);
  static void afs_invalidatepage(struct page *page, unsigned long offset);
  static int afs_releasepage(struct page *page, gfp_t gfp_flags);
31143d5d5   David Howells   AFS: implement ba...
23
  static int afs_launder_page(struct page *page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24

9b3f26c91   David Howells   FS-Cache: Make kA...
25
26
  static int afs_readpages(struct file *filp, struct address_space *mapping,
  			 struct list_head *pages, unsigned nr_pages);
00d3b7a45   David Howells   [AFS]: Add securi...
27
28
29
30
31
  const struct file_operations afs_file_operations = {
  	.open		= afs_open,
  	.release	= afs_release,
  	.llseek		= generic_file_llseek,
  	.read		= do_sync_read,
31143d5d5   David Howells   AFS: implement ba...
32
  	.write		= do_sync_write,
00d3b7a45   David Howells   [AFS]: Add securi...
33
  	.aio_read	= generic_file_aio_read,
31143d5d5   David Howells   AFS: implement ba...
34
  	.aio_write	= afs_file_write,
00d3b7a45   David Howells   [AFS]: Add securi...
35
  	.mmap		= generic_file_readonly_mmap,
5ffc4ef45   Jens Axboe   sendfile: remove ...
36
  	.splice_read	= generic_file_splice_read,
31143d5d5   David Howells   AFS: implement ba...
37
  	.fsync		= afs_fsync,
e8d6c5541   David Howells   AFS: implement fi...
38
39
  	.lock		= afs_lock,
  	.flock		= afs_flock,
00d3b7a45   David Howells   [AFS]: Add securi...
40
  };
754661f14   Arjan van de Ven   [PATCH] mark stru...
41
  const struct inode_operations afs_file_inode_operations = {
416351f28   David Howells   AFS: AFS fixups
42
  	.getattr	= afs_getattr,
31143d5d5   David Howells   AFS: implement ba...
43
  	.setattr	= afs_setattr,
00d3b7a45   David Howells   [AFS]: Add securi...
44
  	.permission	= afs_permission,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
  };
f5e54d6e5   Christoph Hellwig   [PATCH] mark addr...
46
  const struct address_space_operations afs_fs_aops = {
416351f28   David Howells   AFS: AFS fixups
47
  	.readpage	= afs_readpage,
9b3f26c91   David Howells   FS-Cache: Make kA...
48
  	.readpages	= afs_readpages,
31143d5d5   David Howells   AFS: implement ba...
49
50
  	.set_page_dirty	= afs_set_page_dirty,
  	.launder_page	= afs_launder_page,
416351f28   David Howells   AFS: AFS fixups
51
52
  	.releasepage	= afs_releasepage,
  	.invalidatepage	= afs_invalidatepage,
15b4650e5   Nick Piggin   afs: convert to n...
53
54
  	.write_begin	= afs_write_begin,
  	.write_end	= afs_write_end,
31143d5d5   David Howells   AFS: implement ba...
55
56
  	.writepage	= afs_writepage,
  	.writepages	= afs_writepages,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
  /*
00d3b7a45   David Howells   [AFS]: Add securi...
59
60
61
62
63
64
   * open an AFS file or directory and attach a key to it
   */
  int afs_open(struct inode *inode, struct file *file)
  {
  	struct afs_vnode *vnode = AFS_FS_I(inode);
  	struct key *key;
260a98031   David Howells   [AFS]: Add "direc...
65
  	int ret;
00d3b7a45   David Howells   [AFS]: Add securi...
66

416351f28   David Howells   AFS: AFS fixups
67
  	_enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode);
00d3b7a45   David Howells   [AFS]: Add securi...
68
69
70
71
72
73
  
  	key = afs_request_key(vnode->volume->cell);
  	if (IS_ERR(key)) {
  		_leave(" = %ld [key]", PTR_ERR(key));
  		return PTR_ERR(key);
  	}
260a98031   David Howells   [AFS]: Add "direc...
74
75
76
77
78
  	ret = afs_validate(vnode, key);
  	if (ret < 0) {
  		_leave(" = %d [val]", ret);
  		return ret;
  	}
00d3b7a45   David Howells   [AFS]: Add securi...
79
80
81
82
83
84
85
86
87
88
89
  	file->private_data = key;
  	_leave(" = 0");
  	return 0;
  }
  
  /*
   * release an AFS file or directory and discard its key
   */
  int afs_release(struct inode *inode, struct file *file)
  {
  	struct afs_vnode *vnode = AFS_FS_I(inode);
416351f28   David Howells   AFS: AFS fixups
90
  	_enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode);
00d3b7a45   David Howells   [AFS]: Add securi...
91
92
93
94
95
  
  	key_put(file->private_data);
  	_leave(" = 0");
  	return 0;
  }
6566abdbd   Matt Kraai   AFS: Guard afs_fi...
96
  #ifdef CONFIG_AFS_FSCACHE
00d3b7a45   David Howells   [AFS]: Add securi...
97
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
   * deal with notification that a page was read from the cache
   */
9b3f26c91   David Howells   FS-Cache: Make kA...
100
101
102
  static void afs_file_readpage_read_complete(struct page *page,
  					    void *data,
  					    int error)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
  {
9b3f26c91   David Howells   FS-Cache: Make kA...
104
  	_enter("%p,%p,%d", page, data, error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105

9b3f26c91   David Howells   FS-Cache: Make kA...
106
107
108
  	/* if the read completes with an error, we just unlock the page and let
  	 * the VM reissue the readpage */
  	if (!error)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
  		SetPageUptodate(page);
  	unlock_page(page);
ec26815ad   David Howells   [AFS]: Clean up t...
111
  }
6566abdbd   Matt Kraai   AFS: Guard afs_fi...
112
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  /*
f6d335c08   Al Viro   AFS: Don't put st...
115
   * read page from file, directory or symlink, given a key to use
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
   */
f6d335c08   Al Viro   AFS: Don't put st...
117
  int afs_page_filler(void *data, struct page *page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
  {
f6d335c08   Al Viro   AFS: Don't put st...
119
120
121
  	struct inode *inode = page->mapping->host;
  	struct afs_vnode *vnode = AFS_FS_I(inode);
  	struct key *key = data;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
122
123
  	size_t len;
  	off_t offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  	int ret;
00d3b7a45   David Howells   [AFS]: Add securi...
125
  	_enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126

cd7619d6b   Matt Mackall   [PATCH] Extermina...
127
  	BUG_ON(!PageLocked(page));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
  
  	ret = -ESTALE;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
130
  	if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  	/* is it cached? */
9b3f26c91   David Howells   FS-Cache: Make kA...
133
134
  #ifdef CONFIG_AFS_FSCACHE
  	ret = fscache_read_or_alloc_page(vnode->cache,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
140
141
  					 page,
  					 afs_file_readpage_read_complete,
  					 NULL,
  					 GFP_KERNEL);
  #else
  	ret = -ENOBUFS;
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  	switch (ret) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
  		/* read BIO submitted (page in cache) */
  	case 0:
  		break;
9b3f26c91   David Howells   FS-Cache: Make kA...
146
  		/* page not yet cached */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
  	case -ENODATA:
9b3f26c91   David Howells   FS-Cache: Make kA...
148
149
150
151
152
153
  		_debug("cache said ENODATA");
  		goto go_on;
  
  		/* page will not be cached */
  	case -ENOBUFS:
  		_debug("cache said ENOBUFS");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  	default:
9b3f26c91   David Howells   FS-Cache: Make kA...
155
  	go_on:
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
156
157
  		offset = page->index << PAGE_CACHE_SHIFT;
  		len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
  
  		/* read the contents of the file from the server into the
  		 * page */
00d3b7a45   David Howells   [AFS]: Add securi...
161
  		ret = afs_vnode_fetch_data(vnode, key, offset, len, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  		if (ret < 0) {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
163
  			if (ret == -ENOENT) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
  				_debug("got NOENT from server"
  				       " - marking file deleted and stale");
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
166
  				set_bit(AFS_VNODE_DELETED, &vnode->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
  				ret = -ESTALE;
  			}
9b3f26c91   David Howells   FS-Cache: Make kA...
169
170
171
  
  #ifdef CONFIG_AFS_FSCACHE
  			fscache_uncache_page(vnode->cache, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  #endif
9b3f26c91   David Howells   FS-Cache: Make kA...
173
  			BUG_ON(PageFsCache(page));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
  			goto error;
  		}
  
  		SetPageUptodate(page);
9b3f26c91   David Howells   FS-Cache: Make kA...
178
179
180
181
182
183
  		/* send the page to the cache */
  #ifdef CONFIG_AFS_FSCACHE
  		if (PageFsCache(page) &&
  		    fscache_write_page(vnode->cache, page, GFP_KERNEL) != 0) {
  			fscache_uncache_page(vnode->cache, page);
  			BUG_ON(PageFsCache(page));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  #endif
9b3f26c91   David Howells   FS-Cache: Make kA...
186
  		unlock_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
  	}
  
  	_leave(" = 0");
  	return 0;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
191
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  	SetPageError(page);
  	unlock_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
  	_leave(" = %d", ret);
  	return ret;
ec26815ad   David Howells   [AFS]: Clean up t...
196
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
  /*
f6d335c08   Al Viro   AFS: Don't put st...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
   * read page from file, directory or symlink, given a file to nominate the key
   * to be used
   */
  static int afs_readpage(struct file *file, struct page *page)
  {
  	struct key *key;
  	int ret;
  
  	if (file) {
  		key = file->private_data;
  		ASSERT(key != NULL);
  		ret = afs_page_filler(key, page);
  	} else {
  		struct inode *inode = page->mapping->host;
  		key = afs_request_key(AFS_FS_S(inode->i_sb)->volume->cell);
  		if (IS_ERR(key)) {
  			ret = PTR_ERR(key);
  		} else {
  			ret = afs_page_filler(key, page);
  			key_put(key);
  		}
  	}
  	return ret;
  }
  
  /*
9b3f26c91   David Howells   FS-Cache: Make kA...
225
   * read a set of pages
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
   */
9b3f26c91   David Howells   FS-Cache: Make kA...
227
228
  static int afs_readpages(struct file *file, struct address_space *mapping,
  			 struct list_head *pages, unsigned nr_pages)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
  {
f6d335c08   Al Viro   AFS: Don't put st...
230
  	struct key *key = file->private_data;
9b3f26c91   David Howells   FS-Cache: Make kA...
231
232
  	struct afs_vnode *vnode;
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233

f6d335c08   Al Viro   AFS: Don't put st...
234
235
236
237
  	_enter("{%d},{%lu},,%d",
  	       key_serial(key), mapping->host->i_ino, nr_pages);
  
  	ASSERT(key != NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238

9b3f26c91   David Howells   FS-Cache: Make kA...
239
240
241
242
243
  	vnode = AFS_FS_I(mapping->host);
  	if (vnode->flags & AFS_VNODE_DELETED) {
  		_leave(" = -ESTALE");
  		return -ESTALE;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244

9b3f26c91   David Howells   FS-Cache: Make kA...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  	/* attempt to read as many of the pages as possible */
  #ifdef CONFIG_AFS_FSCACHE
  	ret = fscache_read_or_alloc_pages(vnode->cache,
  					  mapping,
  					  pages,
  					  &nr_pages,
  					  afs_file_readpage_read_complete,
  					  NULL,
  					  mapping_gfp_mask(mapping));
  #else
  	ret = -ENOBUFS;
  #endif
  
  	switch (ret) {
  		/* all pages are being read from the cache */
  	case 0:
  		BUG_ON(!list_empty(pages));
  		BUG_ON(nr_pages != 0);
  		_leave(" = 0 [reading all]");
  		return 0;
  
  		/* there were pages that couldn't be read from the cache */
  	case -ENODATA:
  	case -ENOBUFS:
  		break;
  
  		/* other error */
  	default:
  		_leave(" = %d", ret);
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  	}
9b3f26c91   David Howells   FS-Cache: Make kA...
276
  	/* load the missing pages from the network */
f6d335c08   Al Viro   AFS: Don't put st...
277
  	ret = read_cache_pages(mapping, pages, afs_page_filler, key);
9b3f26c91   David Howells   FS-Cache: Make kA...
278
279
280
  
  	_leave(" = %d [netting]", ret);
  	return ret;
ec26815ad   David Howells   [AFS]: Clean up t...
281
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
  /*
31143d5d5   David Howells   AFS: implement ba...
284
285
286
287
288
289
290
291
292
293
   * write back a dirty page
   */
  static int afs_launder_page(struct page *page)
  {
  	_enter("{%lu}", page->index);
  
  	return 0;
  }
  
  /*
9b3f26c91   David Howells   FS-Cache: Make kA...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
   * invalidate part or all of a page
   * - release a page and clean up its private data if offset is 0 (indicating
   *   the entire page)
   */
  static void afs_invalidatepage(struct page *page, unsigned long offset)
  {
  	struct afs_writeback *wb = (struct afs_writeback *) page_private(page);
  
  	_enter("{%lu},%lu", page->index, offset);
  
  	BUG_ON(!PageLocked(page));
  
  	/* we clean up only if the entire page is being invalidated */
  	if (offset == 0) {
  #ifdef CONFIG_AFS_FSCACHE
  		if (PageFsCache(page)) {
  			struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
  			fscache_wait_on_page_write(vnode->cache, page);
  			fscache_uncache_page(vnode->cache, page);
9b3f26c91   David Howells   FS-Cache: Make kA...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
  		}
  #endif
  
  		if (PagePrivate(page)) {
  			if (wb && !PageWriteback(page)) {
  				set_page_private(page, 0);
  				afs_put_writeback(wb);
  			}
  
  			if (!page_private(page))
  				ClearPagePrivate(page);
  		}
  	}
  
  	_leave("");
  }
  
  /*
   * release a page and clean up its private state if it's not busy
   * - return true if the page can now be released, false if not
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
   */
416351f28   David Howells   AFS: AFS fixups
334
  static int afs_releasepage(struct page *page, gfp_t gfp_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
  {
9b3f26c91   David Howells   FS-Cache: Make kA...
336
  	struct afs_writeback *wb = (struct afs_writeback *) page_private(page);
416351f28   David Howells   AFS: AFS fixups
337
  	struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338

416351f28   David Howells   AFS: AFS fixups
339
340
341
  	_enter("{{%x:%u}[%lu],%lx},%x",
  	       vnode->fid.vid, vnode->fid.vnode, page->index, page->flags,
  	       gfp_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342

9b3f26c91   David Howells   FS-Cache: Make kA...
343
344
345
  	/* deny if page is being written to the cache and the caller hasn't
  	 * elected to wait */
  #ifdef CONFIG_AFS_FSCACHE
201a15428   David Howells   FS-Cache: Handle ...
346
347
348
  	if (!fscache_maybe_release_page(vnode->cache, page, gfp_flags)) {
  		_leave(" = F [cache busy]");
  		return 0;
9b3f26c91   David Howells   FS-Cache: Make kA...
349
350
  	}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  	if (PagePrivate(page)) {
9b3f26c91   David Howells   FS-Cache: Make kA...
352
353
354
355
  		if (wb) {
  			set_page_private(page, 0);
  			afs_put_writeback(wb);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  		ClearPagePrivate(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  	}
9b3f26c91   David Howells   FS-Cache: Make kA...
358
359
360
  	/* indicate that the page can be released */
  	_leave(" = T");
  	return 1;
ec26815ad   David Howells   [AFS]: Clean up t...
361
  }