file.c 9.25 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3
/*
 * JFFS2 -- Journalling Flash File System, Version 2.
 *
4
 * Copyright © 2001-2007 Red Hat, Inc.
5
 * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org>
Linus Torvalds's avatar
Linus Torvalds committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * Created by David Woodhouse <dwmw2@infradead.org>
 *
 * For licensing information, see the file 'LICENCE' in this directory.
 *
 */

#include <linux/kernel.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"

Nick Piggin's avatar
Nick Piggin committed
22 23 24 25 26 27
static int jffs2_write_end(struct file *filp, struct address_space *mapping,
			loff_t pos, unsigned len, unsigned copied,
			struct page *pg, void *fsdata);
static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
			loff_t pos, unsigned len, unsigned flags,
			struct page **pagep, void **fsdata);
Linus Torvalds's avatar
Linus Torvalds committed
28 29
static int jffs2_readpage (struct file *filp, struct page *pg);

30
int jffs2_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
Linus Torvalds's avatar
Linus Torvalds committed
31
{
32
	struct inode *inode = filp->f_mapping->host;
Linus Torvalds's avatar
Linus Torvalds committed
33
	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
34
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
35

36 37 38 39 40
	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (ret)
		return ret;

	mutex_lock(&inode->i_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
41 42
	/* Trigger GC to flush any pending writes for this inode */
	jffs2_flush_wbuf_gc(c, inode->i_ino);
43
	mutex_unlock(&inode->i_mutex);
44 45

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
46 47
}

48
const struct file_operations jffs2_file_operations =
Linus Torvalds's avatar
Linus Torvalds committed
49 50 51
{
	.llseek =	generic_file_llseek,
	.open =		generic_file_open,
52 53 54 55
 	.read =		do_sync_read,
 	.aio_read =	generic_file_aio_read,
 	.write =	do_sync_write,
 	.aio_write =	generic_file_aio_write,
56
	.unlocked_ioctl=jffs2_ioctl,
Linus Torvalds's avatar
Linus Torvalds committed
57 58
	.mmap =		generic_file_readonly_mmap,
	.fsync =	jffs2_fsync,
59
	.splice_read =	generic_file_splice_read,
Linus Torvalds's avatar
Linus Torvalds committed
60 61 62 63
};

/* jffs2_file_inode_operations */

64
const struct inode_operations jffs2_file_inode_operations =
Linus Torvalds's avatar
Linus Torvalds committed
65
{
66
	.get_acl =	jffs2_get_acl,
67 68 69 70 71
	.setattr =	jffs2_setattr,
	.setxattr =	jffs2_setxattr,
	.getxattr =	jffs2_getxattr,
	.listxattr =	jffs2_listxattr,
	.removexattr =	jffs2_removexattr
Linus Torvalds's avatar
Linus Torvalds committed
72 73
};

74
const struct address_space_operations jffs2_file_address_operations =
Linus Torvalds's avatar
Linus Torvalds committed
75 76
{
	.readpage =	jffs2_readpage,
Nick Piggin's avatar
Nick Piggin committed
77 78
	.write_begin =	jffs2_write_begin,
	.write_end =	jffs2_write_end,
Linus Torvalds's avatar
Linus Torvalds committed
79 80 81 82 83 84 85 86 87
};

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;

88 89
	jffs2_dbg(2, "%s(): ino #%lu, page at offset 0x%lx\n",
		  __func__, inode->i_ino, pg->index << PAGE_CACHE_SHIFT);
Linus Torvalds's avatar
Linus Torvalds committed
90

Matt Mackall's avatar
Matt Mackall committed
91
	BUG_ON(!PageLocked(pg));
Linus Torvalds's avatar
Linus Torvalds committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

	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);

109
	jffs2_dbg(2, "readpage finished\n");
110
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
111 112 113 114 115 116 117 118 119 120 121 122 123 124
}

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;
125

126
	mutex_lock(&f->sem);
Linus Torvalds's avatar
Linus Torvalds committed
127
	ret = jffs2_do_readpage_unlock(pg->mapping->host, pg);
128
	mutex_unlock(&f->sem);
Linus Torvalds's avatar
Linus Torvalds committed
129 130 131
	return ret;
}

Nick Piggin's avatar
Nick Piggin committed
132 133 134
static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
			loff_t pos, unsigned len, unsigned flags,
			struct page **pagep, void **fsdata)
Linus Torvalds's avatar
Linus Torvalds committed
135
{
Nick Piggin's avatar
Nick Piggin committed
136 137
	struct page *pg;
	struct inode *inode = mapping->host;
Linus Torvalds's avatar
Linus Torvalds committed
138
	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
Nick Piggin's avatar
Nick Piggin committed
139
	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
140
	uint32_t pageofs = index << PAGE_CACHE_SHIFT;
Linus Torvalds's avatar
Linus Torvalds committed
141 142
	int ret = 0;

143
	pg = grab_cache_page_write_begin(mapping, index, flags);
Nick Piggin's avatar
Nick Piggin committed
144 145 146 147
	if (!pg)
		return -ENOMEM;
	*pagep = pg;

148
	jffs2_dbg(1, "%s()\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
149 150 151 152 153 154

	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;
155
		uint32_t alloc_len;
156

157 158
		jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
			  (unsigned int)inode->i_size, pageofs);
Linus Torvalds's avatar
Linus Torvalds committed
159

160 161
		ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
					  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
162
		if (ret)
Nick Piggin's avatar
Nick Piggin committed
163
			goto out_page;
Linus Torvalds's avatar
Linus Torvalds committed
164

165
		mutex_lock(&f->sem);
Linus Torvalds's avatar
Linus Torvalds committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
		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);
186

187
		fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_NORMAL);
Linus Torvalds's avatar
Linus Torvalds committed
188 189 190 191

		if (IS_ERR(fn)) {
			ret = PTR_ERR(fn);
			jffs2_complete_reservation(c);
192
			mutex_unlock(&f->sem);
Nick Piggin's avatar
Nick Piggin committed
193
			goto out_page;
Linus Torvalds's avatar
Linus Torvalds committed
194 195 196 197 198 199 200 201
		}
		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) {
202 203
			jffs2_dbg(1, "Eep. add_full_dnode_to_inode() failed in write_begin, returned %d\n",
				  ret);
Linus Torvalds's avatar
Linus Torvalds committed
204 205 206
			jffs2_mark_node_obsolete(c, fn->raw);
			jffs2_free_full_dnode(fn);
			jffs2_complete_reservation(c);
207
			mutex_unlock(&f->sem);
Nick Piggin's avatar
Nick Piggin committed
208
			goto out_page;
Linus Torvalds's avatar
Linus Torvalds committed
209 210 211
		}
		jffs2_complete_reservation(c);
		inode->i_size = pageofs;
212
		mutex_unlock(&f->sem);
Linus Torvalds's avatar
Linus Torvalds committed
213
	}
214

Nick Piggin's avatar
Nick Piggin committed
215 216 217 218 219 220
	/*
	 * Read in the page if it wasn't already present. Cannot optimize away
	 * the whole page write case until jffs2_write_end can handle the
	 * case of a short-copy.
	 */
	if (!PageUptodate(pg)) {
221
		mutex_lock(&f->sem);
Linus Torvalds's avatar
Linus Torvalds committed
222
		ret = jffs2_do_readpage_nolock(inode, pg);
223
		mutex_unlock(&f->sem);
Nick Piggin's avatar
Nick Piggin committed
224 225
		if (ret)
			goto out_page;
Linus Torvalds's avatar
Linus Torvalds committed
226
	}
227
	jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags);
Nick Piggin's avatar
Nick Piggin committed
228 229 230 231 232
	return ret;

out_page:
	unlock_page(pg);
	page_cache_release(pg);
Linus Torvalds's avatar
Linus Torvalds committed
233 234 235
	return ret;
}

Nick Piggin's avatar
Nick Piggin committed
236 237 238
static int jffs2_write_end(struct file *filp, struct address_space *mapping,
			loff_t pos, unsigned len, unsigned copied,
			struct page *pg, void *fsdata)
Linus Torvalds's avatar
Linus Torvalds committed
239 240 241 242
{
	/* 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
	 */
Nick Piggin's avatar
Nick Piggin committed
243
	struct inode *inode = mapping->host;
Linus Torvalds's avatar
Linus Torvalds committed
244 245 246
	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;
Nick Piggin's avatar
Nick Piggin committed
247 248
	unsigned start = pos & (PAGE_CACHE_SIZE - 1);
	unsigned end = start + copied;
Linus Torvalds's avatar
Linus Torvalds committed
249 250 251 252
	unsigned aligned_start = start & ~3;
	int ret = 0;
	uint32_t writtenlen = 0;

253 254 255
	jffs2_dbg(1, "%s(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
		  __func__, inode->i_ino, pg->index << PAGE_CACHE_SHIFT,
		  start, end, pg->flags);
Linus Torvalds's avatar
Linus Torvalds committed
256

Nick Piggin's avatar
Nick Piggin committed
257 258 259 260 261 262
	/* We need to avoid deadlock with page_cache_read() in
	   jffs2_garbage_collect_pass(). So the page must be
	   up to date to prevent page_cache_read() from trying
	   to re-lock it. */
	BUG_ON(!PageUptodate(pg));

263
	if (end == PAGE_CACHE_SIZE) {
Nick Piggin's avatar
Nick Piggin committed
264 265 266 267
		/* 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. */
268
		aligned_start = 0;
Linus Torvalds's avatar
Linus Torvalds committed
269 270 271 272 273
	}

	ri = jffs2_alloc_raw_inode();

	if (!ri) {
274 275
		jffs2_dbg(1, "%s(): Allocation of raw inode failed\n",
			  __func__);
Nick Piggin's avatar
Nick Piggin committed
276 277
		unlock_page(pg);
		page_cache_release(pg);
Linus Torvalds's avatar
Linus Torvalds committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
		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);
	}
303

Linus Torvalds's avatar
Linus Torvalds committed
304
	/* Adjust writtenlen for the padding we did, so we don't confuse our caller */
305
	writtenlen -= min(writtenlen, (start - aligned_start));
Linus Torvalds's avatar
Linus Torvalds committed
306 307

	if (writtenlen) {
308 309
		if (inode->i_size < pos + writtenlen) {
			inode->i_size = pos + writtenlen;
Linus Torvalds's avatar
Linus Torvalds committed
310
			inode->i_blocks = (inode->i_size + 511) >> 9;
311

Linus Torvalds's avatar
Linus Torvalds committed
312 313 314 315 316 317 318 319
			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
320
		   actually written to the medium. Mark the page !Uptodate so that
Linus Torvalds's avatar
Linus Torvalds committed
321
		   it gets reread */
322 323
		jffs2_dbg(1, "%s(): Not all bytes written. Marking page !uptodate\n",
			__func__);
Linus Torvalds's avatar
Linus Torvalds committed
324 325 326 327
		SetPageError(pg);
		ClearPageUptodate(pg);
	}

328 329
	jffs2_dbg(1, "%s() returning %d\n",
		  __func__, writtenlen > 0 ? writtenlen : ret);
Nick Piggin's avatar
Nick Piggin committed
330 331 332
	unlock_page(pg);
	page_cache_release(pg);
	return writtenlen > 0 ? writtenlen : ret;
Linus Torvalds's avatar
Linus Torvalds committed
333
}