Blame view

fs/jffs2/file.c 8.54 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
c00c310ea   David Woodhouse   [JFFS2] Tidy up l...
4
   * Copyright © 2001-2007 Red Hat, Inc.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
   *
   * Created by David Woodhouse <dwmw2@infradead.org>
   *
   * For licensing information, see the file 'LICENCE' in this directory.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
15
16
17
18
19
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/fs.h>
  #include <linux/time.h>
  #include <linux/pagemap.h>
  #include <linux/highmem.h>
  #include <linux/crc32.h>
  #include <linux/jffs2.h>
  #include "nodelist.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
22
23
24
25
26
27
28
29
30
31
32
  static int jffs2_commit_write (struct file *filp, struct page *pg,
  			       unsigned start, unsigned end);
  static int jffs2_prepare_write (struct file *filp, struct page *pg,
  				unsigned start, unsigned end);
  static int jffs2_readpage (struct file *filp, struct page *pg);
  
  int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
  {
  	struct inode *inode = dentry->d_inode;
  	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
  
  	/* Trigger GC to flush any pending writes for this inode */
  	jffs2_flush_wbuf_gc(c, inode->i_ino);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
33
34
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
36
  const struct file_operations jffs2_file_operations =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
  {
  	.llseek =	generic_file_llseek,
  	.open =		generic_file_open,
543ade1fc   Badari Pulavarty   [PATCH] Streamlin...
40
41
42
43
   	.read =		do_sync_read,
   	.aio_read =	generic_file_aio_read,
   	.write =	do_sync_write,
   	.aio_write =	generic_file_aio_write,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
  	.ioctl =	jffs2_ioctl,
  	.mmap =		generic_file_readonly_mmap,
  	.fsync =	jffs2_fsync,
5ffc4ef45   Jens Axboe   sendfile: remove ...
47
  	.splice_read =	generic_file_splice_read,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
  };
  
  /* jffs2_file_inode_operations */
92e1d5be9   Arjan van de Ven   [PATCH] mark stru...
51
  const struct inode_operations jffs2_file_inode_operations =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
  {
aa98d7cf5   KaiGai Kohei   [JFFS2][XATTR] XA...
53
54
55
56
57
58
  	.permission =	jffs2_permission,
  	.setattr =	jffs2_setattr,
  	.setxattr =	jffs2_setxattr,
  	.getxattr =	jffs2_getxattr,
  	.listxattr =	jffs2_listxattr,
  	.removexattr =	jffs2_removexattr
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  };
f5e54d6e5   Christoph Hellwig   [PATCH] mark addr...
60
  const struct address_space_operations jffs2_file_address_operations =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  {
  	.readpage =	jffs2_readpage,
  	.prepare_write =jffs2_prepare_write,
  	.commit_write =	jffs2_commit_write
  };
  
  static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
  {
  	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
  	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
  	unsigned char *pg_buf;
  	int ret;
  
  	D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx
  ", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
cd7619d6b   Matt Mackall   [PATCH] Extermina...
76
  	BUG_ON(!PageLocked(pg));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  
  	pg_buf = kmap(pg);
  	/* FIXME: Can kmap fail? */
  
  	ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
  
  	if (ret) {
  		ClearPageUptodate(pg);
  		SetPageError(pg);
  	} else {
  		SetPageUptodate(pg);
  		ClearPageError(pg);
  	}
  
  	flush_dcache_page(pg);
  	kunmap(pg);
  
  	D2(printk(KERN_DEBUG "readpage finished
  "));
  	return 0;
  }
  
  int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
  {
  	int ret = jffs2_do_readpage_nolock(inode, pg);
  	unlock_page(pg);
  	return ret;
  }
  
  
  static int jffs2_readpage (struct file *filp, struct page *pg)
  {
  	struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host);
  	int ret;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
111

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  	down(&f->sem);
  	ret = jffs2_do_readpage_unlock(pg->mapping->host, pg);
  	up(&f->sem);
  	return ret;
  }
  
  static int jffs2_prepare_write (struct file *filp, struct page *pg,
  				unsigned start, unsigned end)
  {
  	struct inode *inode = pg->mapping->host;
  	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
  	uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
  	int ret = 0;
  
  	D1(printk(KERN_DEBUG "jffs2_prepare_write()
  "));
  
  	if (pageofs > inode->i_size) {
  		/* Make new hole frag from old EOF to new page */
  		struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
  		struct jffs2_raw_inode ri;
  		struct jffs2_full_dnode *fn;
9fe4854cd   David Woodhouse   [JFFS2] Remove fl...
134
  		uint32_t alloc_len;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
135

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
  		D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page
  ",
  			  (unsigned int)inode->i_size, pageofs));
9fe4854cd   David Woodhouse   [JFFS2] Remove fl...
139
140
  		ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
  					  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  		if (ret)
  			return ret;
  
  		down(&f->sem);
  		memset(&ri, 0, sizeof(ri));
  
  		ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
  		ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
  		ri.totlen = cpu_to_je32(sizeof(ri));
  		ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
  
  		ri.ino = cpu_to_je32(f->inocache->ino);
  		ri.version = cpu_to_je32(++f->highest_version);
  		ri.mode = cpu_to_jemode(inode->i_mode);
  		ri.uid = cpu_to_je16(inode->i_uid);
  		ri.gid = cpu_to_je16(inode->i_gid);
  		ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
  		ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds());
  		ri.offset = cpu_to_je32(inode->i_size);
  		ri.dsize = cpu_to_je32(pageofs - inode->i_size);
  		ri.csize = cpu_to_je32(0);
  		ri.compr = JFFS2_COMPR_ZERO;
  		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
  		ri.data_crc = cpu_to_je32(0);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
165

9fe4854cd   David Woodhouse   [JFFS2] Remove fl...
166
  		fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_NORMAL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  
  		if (IS_ERR(fn)) {
  			ret = PTR_ERR(fn);
  			jffs2_complete_reservation(c);
  			up(&f->sem);
  			return ret;
  		}
  		ret = jffs2_add_full_dnode_to_inode(c, f, fn);
  		if (f->metadata) {
  			jffs2_mark_node_obsolete(c, f->metadata->raw);
  			jffs2_free_full_dnode(f->metadata);
  			f->metadata = NULL;
  		}
  		if (ret) {
  			D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d
  ", ret));
  			jffs2_mark_node_obsolete(c, fn->raw);
  			jffs2_free_full_dnode(fn);
  			jffs2_complete_reservation(c);
  			up(&f->sem);
  			return ret;
  		}
  		jffs2_complete_reservation(c);
  		inode->i_size = pageofs;
  		up(&f->sem);
  	}
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
193

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  	/* Read in the page if it wasn't already present, unless it's a whole page */
  	if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
  		down(&f->sem);
  		ret = jffs2_do_readpage_nolock(inode, pg);
  		up(&f->sem);
  	}
  	D1(printk(KERN_DEBUG "end prepare_write(). pg->flags %lx
  ", pg->flags));
  	return ret;
  }
  
  static int jffs2_commit_write (struct file *filp, struct page *pg,
  			       unsigned start, unsigned end)
  {
  	/* Actually commit the write from the page cache page we're looking at.
  	 * For now, we write the full page out each time. It sucks, but it's simple
  	 */
  	struct inode *inode = pg->mapping->host;
  	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
  	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
  	struct jffs2_raw_inode *ri;
  	unsigned aligned_start = start & ~3;
  	int ret = 0;
  	uint32_t writtenlen = 0;
  
  	D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx
  ",
  		  inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
cf5eba533   David Woodhouse   [JFFS2] Reduce ex...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  	if (end == PAGE_CACHE_SIZE) {
  		if (!start) {
  			/* We need to avoid deadlock with page_cache_read() in
  			   jffs2_garbage_collect_pass(). So we have to mark the
  			   page up to date, to prevent page_cache_read() from
  			   trying to re-lock it. */
  			SetPageUptodate(pg);
  		} else {
  			/* When writing out the end of a page, write out the 
  			   _whole_ page. This helps to reduce the number of
  			   nodes in files which have many short writes, like
  			   syslog files. */
  			start = aligned_start = 0;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  	}
  
  	ri = jffs2_alloc_raw_inode();
  
  	if (!ri) {
  		D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed
  "));
  		return -ENOMEM;
  	}
  
  	/* Set the fields that the generic jffs2_write_inode_range() code can't find */
  	ri->ino = cpu_to_je32(inode->i_ino);
  	ri->mode = cpu_to_jemode(inode->i_mode);
  	ri->uid = cpu_to_je16(inode->i_uid);
  	ri->gid = cpu_to_je16(inode->i_gid);
  	ri->isize = cpu_to_je32((uint32_t)inode->i_size);
  	ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds());
  
  	/* In 2.4, it was already kmapped by generic_file_write(). Doesn't
  	   hurt to do it again. The alternative is ifdefs, which are ugly. */
  	kmap(pg);
  
  	ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
  				      (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
  				      end - aligned_start, &writtenlen);
  
  	kunmap(pg);
  
  	if (ret) {
  		/* There was an error writing. */
  		SetPageError(pg);
  	}
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
268

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
276
277
278
  	/* Adjust writtenlen for the padding we did, so we don't confuse our caller */
  	if (writtenlen < (start&3))
  		writtenlen = 0;
  	else
  		writtenlen -= (start&3);
  
  	if (writtenlen) {
  		if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
  			inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
  			inode->i_blocks = (inode->i_size + 511) >> 9;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
279

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
285
286
287
  			inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
  		}
  	}
  
  	jffs2_free_raw_inode(ri);
  
  	if (start+writtenlen < end) {
  		/* generic_file_write has written more to the page cache than we've
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
288
  		   actually written to the medium. Mark the page !Uptodate so that
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
293
294
  		   it gets reread */
  		D1(printk(KERN_DEBUG "jffs2_commit_write(): Not all bytes written. Marking page !uptodate
  "));
  		SetPageError(pg);
  		ClearPageUptodate(pg);
  	}
4fc67fbe5   Todd Poynor   [JFFS2] Return 0,...
295
296
297
  	D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d
  ",start+writtenlen==end?0:ret));
  	return start+writtenlen==end?0:ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  }