file.c 5.58 KB
Newer Older
1 2 3
/*
 *  linux/fs/ext2/file.c
 *
Linus Torvalds's avatar
Linus Torvalds committed
4 5 6 7
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI - Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
8 9 10 11 12 13 14 15
 *
 *  from
 *
 *  linux/fs/minix/file.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  ext2 fs regular file handling primitives
Linus Torvalds's avatar
Linus Torvalds committed
16 17 18
 *
 *  64-bit file support on 64-bit platforms by Jakub Jelinek
 * 	(jj@sunsite.ms.mff.cuni.cz)
19 20
 */

Linus Torvalds's avatar
Linus Torvalds committed
21
#include <asm/uaccess.h>
22 23 24
#include <asm/system.h>

#include <linux/errno.h>
25 26
#include <linux/fs.h>
#include <linux/ext2_fs.h>
27
#include <linux/fcntl.h>
28
#include <linux/sched.h>
29 30
#include <linux/stat.h>
#include <linux/locks.h>
Linus Torvalds's avatar
Linus Torvalds committed
31 32
#include <linux/mm.h>
#include <linux/pagemap.h>
Linus Torvalds's avatar
Linus Torvalds committed
33
#include <linux/smp_lock.h>
34

Linus Torvalds's avatar
Linus Torvalds committed
35
#define	NBUF	32
36 37 38 39

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

Linus Torvalds's avatar
Linus Torvalds committed
40
static int ext2_writepage (struct file * file, struct page * page);
Linus Torvalds's avatar
Linus Torvalds committed
41
static long long ext2_file_lseek(struct file *, long long, int);
Linus Torvalds's avatar
Linus Torvalds committed
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
#if BITS_PER_LONG < 64
static int ext2_open_file (struct inode *, struct file *);

#else

#define EXT2_MAX_SIZE(bits)							\
	(((EXT2_NDIR_BLOCKS + (1LL << (bits - 2)) + 				\
	   (1LL << (bits - 2)) * (1LL << (bits - 2)) + 				\
	   (1LL << (bits - 2)) * (1LL << (bits - 2)) * (1LL << (bits - 2))) * 	\
	  (1LL << bits)) - 1)

static long long ext2_max_sizes[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
EXT2_MAX_SIZE(10), EXT2_MAX_SIZE(11), EXT2_MAX_SIZE(12), EXT2_MAX_SIZE(13)
};

#endif
59 60


Linus Torvalds's avatar
Linus Torvalds committed
61 62 63
/*
 * Make sure the offset never goes beyond the 32-bit mark..
 */
Linus Torvalds's avatar
Linus Torvalds committed
64
static long long ext2_file_lseek(
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68
	struct file *file,
	long long offset,
	int origin)
{
Linus Torvalds's avatar
Linus Torvalds committed
69
	struct inode *inode = file->f_dentry->d_inode;
Linus Torvalds's avatar
Linus Torvalds committed
70 71 72 73 74 75 76 77

	switch (origin) {
		case 2:
			offset += inode->i_size;
			break;
		case 1:
			offset += file->f_pos;
	}
Linus Torvalds's avatar
Linus Torvalds committed
78 79 80 81 82 83 84 85 86 87 88 89
	if (((unsigned long long) offset >> 32) != 0) {
#if BITS_PER_LONG < 64
		return -EINVAL;
#else
		if (offset > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(inode->i_sb)])
			return -EINVAL;
#endif
	} 
	if (offset != file->f_pos) {
		file->f_pos = offset;
		file->f_reada = 0;
		file->f_version = ++event;
Linus Torvalds's avatar
Linus Torvalds committed
90
	}
Linus Torvalds's avatar
Linus Torvalds committed
91
	return offset;
Linus Torvalds's avatar
Linus Torvalds committed
92 93
}

Linus Torvalds's avatar
Linus Torvalds committed
94 95 96 97 98 99 100 101 102
static inline void remove_suid(struct inode *inode)
{
	unsigned int mode;

	/* set S_IGID if S_IXGRP is set, and always set S_ISUID */
	mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID;

	/* was any of the uid bits set? */
	mode &= inode->i_mode;
Linus Torvalds's avatar
Linus Torvalds committed
103
	if (mode && !capable(CAP_FSETID)) {
Linus Torvalds's avatar
Linus Torvalds committed
104
		inode->i_mode &= ~mode;
Linus Torvalds's avatar
Linus Torvalds committed
105
		mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
106 107 108
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
static int ext2_get_block(struct inode *inode, unsigned long block, struct buffer_head *bh, int update)
{
	if (!bh->b_blocknr) {
		int error, created;
		unsigned long blocknr;

		blocknr = ext2_getblk_block(inode, block, 1, &error, &created);
		if (!blocknr)
			return error;

		bh->b_dev = inode->i_dev;
		bh->b_blocknr = blocknr;

		if (!update)
			return 0;

		if (created) {
			memset(bh->b_data, 0, bh->b_size);
			set_bit(BH_Uptodate, &bh->b_state);
			return 0;
		}
	}

	if (!update)
		return 0;

	lock_kernel();
	ll_rw_block(READ, 1, &bh);
	wait_on_buffer(bh);
	unlock_kernel();

	return buffer_uptodate(bh) ? 0 : -EIO;
}

Linus Torvalds's avatar
Linus Torvalds committed
143
static int ext2_writepage (struct file * file, struct page * page)
144
{
Linus Torvalds's avatar
Linus Torvalds committed
145
	return block_write_full_page(file, page, ext2_get_block);
Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149
}

static long ext2_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
{
Linus Torvalds's avatar
Linus Torvalds committed
150
	return block_write_partial_page(file, page, offset, bytes, buf, ext2_get_block);
Linus Torvalds's avatar
Linus Torvalds committed
151 152 153 154 155 156 157 158
}

/*
 * Write to a file (through the page cache).
 */
static ssize_t
ext2_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
159 160 161 162 163 164 165 166
	ssize_t retval = generic_file_write(file, buf, count, ppos, ext2_write_one_page);
	if (retval > 0) {
		struct inode *inode = file->f_dentry->d_inode;
		remove_suid(inode);
		inode->i_ctime = inode->i_mtime = CURRENT_TIME;
		mark_inode_dirty(inode);
	}
	return retval;
167
}
Linus Torvalds's avatar
Linus Torvalds committed
168 169

/*
Linus Torvalds's avatar
Linus Torvalds committed
170
 * Called when an inode is released. Note that this is different
Linus Torvalds's avatar
Linus Torvalds committed
171
 * from ext2_file_open: open gets called at every open, but release
Linus Torvalds's avatar
Linus Torvalds committed
172 173
 * gets called only when /all/ the files are closed.
 */
Linus Torvalds's avatar
Linus Torvalds committed
174
static int ext2_release_file (struct inode * inode, struct file * filp)
Linus Torvalds's avatar
Linus Torvalds committed
175
{
Linus Torvalds's avatar
Linus Torvalds committed
176
	if (filp->f_mode & FMODE_WRITE)
Linus Torvalds's avatar
Linus Torvalds committed
177
		ext2_discard_prealloc (inode);
Linus Torvalds's avatar
Linus Torvalds committed
178
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
179
}
Linus Torvalds's avatar
Linus Torvalds committed
180 181 182 183 184 185 186 187 188 189 190 191 192

#if BITS_PER_LONG < 64
/*
 * Called when an inode is about to be open.
 * We use this to disallow opening RW large files on 32bit systems.
 */
static int ext2_open_file (struct inode * inode, struct file * filp)
{
	if (inode->u.ext2_i.i_high_size && (filp->f_mode & FMODE_WRITE))
		return -EFBIG;
	return 0;
}
#endif
Linus Torvalds's avatar
Linus Torvalds committed
193 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 222 223 224 225 226 227 228 229 230 231 232

/*
 * We have mostly NULL's here: the current defaults are ok for
 * the ext2 filesystem.
 */
static struct file_operations ext2_file_operations = {
	ext2_file_lseek,	/* lseek */
	generic_file_read,	/* read */
	ext2_file_write,	/* write */
	NULL,			/* readdir - bad */
	NULL,			/* poll - default */
	ext2_ioctl,		/* ioctl */
	generic_file_mmap,	/* mmap */
#if BITS_PER_LONG == 64	
	NULL,			/* no special open is needed */
#else
	ext2_open_file,
#endif
	NULL,			/* flush */
	ext2_release_file,	/* release */
	ext2_sync_file,		/* fsync */
	NULL,			/* fasync */
	NULL,			/* check_media_change */
	NULL			/* revalidate */
};

struct inode_operations ext2_file_inode_operations = {
	&ext2_file_operations,/* default file operations */
	NULL,			/* create */
	NULL,			/* lookup */
	NULL,			/* link */
	NULL,			/* unlink */
	NULL,			/* symlink */
	NULL,			/* mkdir */
	NULL,			/* rmdir */
	NULL,			/* mknod */
	NULL,			/* rename */
	NULL,			/* readlink */
	NULL,			/* follow_link */
	ext2_bmap,		/* bmap */
233 234 235
	block_read_full_page,	/* readpage */
	ext2_writepage,		/* writepage */
	block_flushpage,	/* flushpage */
Linus Torvalds's avatar
Linus Torvalds committed
236 237 238 239 240
	ext2_truncate,		/* truncate */
	ext2_permission,	/* permission */
	NULL,			/* smap */
	NULL,			/* revalidate */
};