inode.c 8.79 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8
/*
 *  linux/fs/hpfs/inode.c
 *
 *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
 *
 *  inode VFS functions
 */

9
#include <linux/slab.h>
10
#include <linux/user_namespace.h>
Linus Torvalds's avatar
Linus Torvalds committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
#include "hpfs_fn.h"

void hpfs_init_inode(struct inode *i)
{
	struct super_block *sb = i->i_sb;
	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);

	i->i_uid = hpfs_sb(sb)->sb_uid;
	i->i_gid = hpfs_sb(sb)->sb_gid;
	i->i_mode = hpfs_sb(sb)->sb_mode;
	i->i_size = -1;
	i->i_blocks = -1;
	
	hpfs_inode->i_dno = 0;
	hpfs_inode->i_n_secs = 0;
	hpfs_inode->i_file_sec = 0;
	hpfs_inode->i_disk_sec = 0;
	hpfs_inode->i_dpos = 0;
	hpfs_inode->i_dsubdno = 0;
	hpfs_inode->i_ea_mode = 0;
	hpfs_inode->i_ea_uid = 0;
	hpfs_inode->i_ea_gid = 0;
	hpfs_inode->i_ea_size = 0;

	hpfs_inode->i_rddir_off = NULL;
	hpfs_inode->i_dirty = 0;

	i->i_ctime.tv_sec = i->i_ctime.tv_nsec = 0;
	i->i_mtime.tv_sec = i->i_mtime.tv_nsec = 0;
	i->i_atime.tv_sec = i->i_atime.tv_nsec = 0;
}

void hpfs_read_inode(struct inode *i)
{
	struct buffer_head *bh;
	struct fnode *fnode;
	struct super_block *sb = i->i_sb;
	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
49
	void *ea;
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52 53 54 55 56
	int ea_size;

	if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) {
		/*i->i_mode |= S_IFREG;
		i->i_mode &= ~0111;
		i->i_op = &hpfs_file_iops;
		i->i_fop = &hpfs_file_ops;
57
		clear_nlink(i);*/
Linus Torvalds's avatar
Linus Torvalds committed
58 59 60 61 62 63
		make_bad_inode(i);
		return;
	}
	if (hpfs_sb(i->i_sb)->sb_eas) {
		if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
			if (ea_size == 2) {
64
				i_uid_write(i, le16_to_cpu(*(__le16*)ea));
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68 69 70
				hpfs_inode->i_ea_uid = 1;
			}
			kfree(ea);
		}
		if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) {
			if (ea_size == 2) {
71
				i_gid_write(i, le16_to_cpu(*(__le16*)ea));
Linus Torvalds's avatar
Linus Torvalds committed
72 73 74 75 76 77 78 79
				hpfs_inode->i_ea_gid = 1;
			}
			kfree(ea);
		}
		if ((ea = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &ea_size))) {
			kfree(ea);
			i->i_mode = S_IFLNK | 0777;
			i->i_op = &page_symlink_inode_operations;
80
			inode_nohighmem(i);
Linus Torvalds's avatar
Linus Torvalds committed
81
			i->i_data.a_ops = &hpfs_symlink_aops;
82
			set_nlink(i, 1);
Linus Torvalds's avatar
Linus Torvalds committed
83 84 85 86 87 88 89 90 91
			i->i_size = ea_size;
			i->i_blocks = 1;
			brelse(bh);
			return;
		}
		if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) {
			int rdev = 0;
			umode_t mode = hpfs_sb(sb)->sb_mode;
			if (ea_size == 2) {
92
				mode = le16_to_cpu(*(__le16*)ea);
Linus Torvalds's avatar
Linus Torvalds committed
93 94 95 96 97 98 99
				hpfs_inode->i_ea_mode = 1;
			}
			kfree(ea);
			i->i_mode = mode;
			if (S_ISBLK(mode) || S_ISCHR(mode)) {
				if ((ea = hpfs_get_ea(i->i_sb, fnode, "DEV", &ea_size))) {
					if (ea_size == 4)
100
						rdev = le32_to_cpu(*(__le32*)ea);
Linus Torvalds's avatar
Linus Torvalds committed
101 102 103 104 105
					kfree(ea);
				}
			}
			if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
				brelse(bh);
106
				set_nlink(i, 1);
Linus Torvalds's avatar
Linus Torvalds committed
107 108 109 110 111 112 113 114
				i->i_size = 0;
				i->i_blocks = 1;
				init_special_inode(i, mode,
					new_decode_dev(rdev));
				return;
			}
		}
	}
115
	if (fnode_is_dir(fnode)) {
116
		int n_dnodes, n_subdirs;
Linus Torvalds's avatar
Linus Torvalds committed
117 118 119
		i->i_mode |= S_IFDIR;
		i->i_op = &hpfs_dir_iops;
		i->i_fop = &hpfs_dir_ops;
120 121
		hpfs_inode->i_parent_dir = le32_to_cpu(fnode->up);
		hpfs_inode->i_dno = le32_to_cpu(fnode->u.external[0].disk_secno);
Linus Torvalds's avatar
Linus Torvalds committed
122 123 124 125 126 127 128 129
		if (hpfs_sb(sb)->sb_chk >= 2) {
			struct buffer_head *bh0;
			if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0);
		}
		n_dnodes = 0; n_subdirs = 0;
		hpfs_count_dnodes(i->i_sb, hpfs_inode->i_dno, &n_dnodes, &n_subdirs, NULL);
		i->i_blocks = 4 * n_dnodes;
		i->i_size = 2048 * n_dnodes;
130
		set_nlink(i, 2 + n_subdirs);
Linus Torvalds's avatar
Linus Torvalds committed
131 132 133 134 135
	} else {
		i->i_mode |= S_IFREG;
		if (!hpfs_inode->i_ea_mode) i->i_mode &= ~0111;
		i->i_op = &hpfs_file_iops;
		i->i_fop = &hpfs_file_ops;
136
		set_nlink(i, 1);
137
		i->i_size = le32_to_cpu(fnode->file_size);
Linus Torvalds's avatar
Linus Torvalds committed
138 139 140 141 142 143 144 145 146 147
		i->i_blocks = ((i->i_size + 511) >> 9) + 1;
		i->i_data.a_ops = &hpfs_aops;
		hpfs_i(i)->mmu_private = i->i_size;
	}
	brelse(bh);
}

static void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
{
	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
148
	/*if (le32_to_cpu(fnode->acl_size_l) || le16_to_cpu(fnode->acl_size_s)) {
Linus Torvalds's avatar
Linus Torvalds committed
149 150
		   Some unknown structures like ACL may be in fnode,
		   we'd better not overwrite them
151
		hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 structures", i->i_ino);
Linus Torvalds's avatar
Linus Torvalds committed
152
	} else*/ if (hpfs_sb(i->i_sb)->sb_eas >= 2) {
153
		__le32 ea;
154 155
		if (!uid_eq(i->i_uid, hpfs_sb(i->i_sb)->sb_uid) || hpfs_inode->i_ea_uid) {
			ea = cpu_to_le32(i_uid_read(i));
Linus Torvalds's avatar
Linus Torvalds committed
156 157 158
			hpfs_set_ea(i, fnode, "UID", (char*)&ea, 2);
			hpfs_inode->i_ea_uid = 1;
		}
159 160
		if (!gid_eq(i->i_gid, hpfs_sb(i->i_sb)->sb_gid) || hpfs_inode->i_ea_gid) {
			ea = cpu_to_le32(i_gid_read(i));
Linus Torvalds's avatar
Linus Torvalds committed
161 162 163 164 165 166 167 168 169
			hpfs_set_ea(i, fnode, "GID", (char *)&ea, 2);
			hpfs_inode->i_ea_gid = 1;
		}
		if (!S_ISLNK(i->i_mode))
			if ((i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
			  && i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) {
				ea = cpu_to_le32(i->i_mode);
170
				/* sick, but legal */
Linus Torvalds's avatar
Linus Torvalds committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
				hpfs_set_ea(i, fnode, "MODE", (char *)&ea, 2);
				hpfs_inode->i_ea_mode = 1;
			}
		if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
			ea = cpu_to_le32(new_encode_dev(i->i_rdev));
			hpfs_set_ea(i, fnode, "DEV", (char *)&ea, 4);
		}
	}
}

void hpfs_write_inode(struct inode *i)
{
	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
	struct inode *parent;
	if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
	if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) {
187
		if (*hpfs_inode->i_rddir_off)
188
			pr_err("write_inode: some position still there\n");
Linus Torvalds's avatar
Linus Torvalds committed
189 190 191 192 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
		kfree(hpfs_inode->i_rddir_off);
		hpfs_inode->i_rddir_off = NULL;
	}
	if (!i->i_nlink) {
		return;
	}
	parent = iget_locked(i->i_sb, hpfs_inode->i_parent_dir);
	if (parent) {
		hpfs_inode->i_dirty = 0;
		if (parent->i_state & I_NEW) {
			hpfs_init_inode(parent);
			hpfs_read_inode(parent);
			unlock_new_inode(parent);
		}
		hpfs_write_inode_nolock(i);
		iput(parent);
	}
}

void hpfs_write_inode_nolock(struct inode *i)
{
	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
	struct buffer_head *bh;
	struct fnode *fnode;
	struct quad_buffer_head qbh;
	struct hpfs_dirent *de;
	if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
	if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return;
	if (i->i_ino != hpfs_sb(i->i_sb)->sb_root && i->i_nlink) {
		if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) {
			brelse(bh);
			return;
		}
	} else de = NULL;
	if (S_ISREG(i->i_mode)) {
224 225
		fnode->file_size = cpu_to_le32(i->i_size);
		if (de) de->file_size = cpu_to_le32(i->i_size);
Linus Torvalds's avatar
Linus Torvalds committed
226
	} else if (S_ISDIR(i->i_mode)) {
227 228
		fnode->file_size = cpu_to_le32(0);
		if (de) de->file_size = cpu_to_le32(0);
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231
	}
	hpfs_write_inode_ea(i, fnode);
	if (de) {
232 233 234
		de->write_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_mtime.tv_sec));
		de->read_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_atime.tv_sec));
		de->creation_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_ctime.tv_sec));
Linus Torvalds's avatar
Linus Torvalds committed
235
		de->read_only = !(i->i_mode & 0222);
236
		de->ea_size = cpu_to_le32(hpfs_inode->i_ea_size);
Linus Torvalds's avatar
Linus Torvalds committed
237 238 239 240 241
		hpfs_mark_4buffers_dirty(&qbh);
		hpfs_brelse4(&qbh);
	}
	if (S_ISDIR(i->i_mode)) {
		if ((de = map_dirent(i, hpfs_inode->i_dno, "\001\001", 2, NULL, &qbh))) {
242 243 244
			de->write_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_mtime.tv_sec));
			de->read_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_atime.tv_sec));
			de->creation_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_ctime.tv_sec));
Linus Torvalds's avatar
Linus Torvalds committed
245
			de->read_only = !(i->i_mode & 0222);
246 247
			de->ea_size = cpu_to_le32(/*hpfs_inode->i_ea_size*/0);
			de->file_size = cpu_to_le32(0);
Linus Torvalds's avatar
Linus Torvalds committed
248 249
			hpfs_mark_4buffers_dirty(&qbh);
			hpfs_brelse4(&qbh);
250 251 252 253
		} else
			hpfs_error(i->i_sb,
				"directory %08lx doesn't have '.' entry",
				(unsigned long)i->i_ino);
Linus Torvalds's avatar
Linus Torvalds committed
254 255 256 257 258
	}
	mark_buffer_dirty(bh);
	brelse(bh);
}

259
int hpfs_setattr(struct dentry *dentry, struct iattr *attr)
Linus Torvalds's avatar
Linus Torvalds committed
260
{
261
	struct inode *inode = d_inode(dentry);
262 263
	int error = -EINVAL;

Arnd Bergmann's avatar
Arnd Bergmann committed
264
	hpfs_lock(inode->i_sb);
265 266
	if (inode->i_ino == hpfs_sb(inode->i_sb)->sb_root)
		goto out_unlock;
267 268
	if ((attr->ia_valid & ATTR_UID) &&
	    from_kuid(&init_user_ns, attr->ia_uid) >= 0x10000)
269
		goto out_unlock;
270 271
	if ((attr->ia_valid & ATTR_GID) &&
	    from_kgid(&init_user_ns, attr->ia_gid) >= 0x10000)
272
		goto out_unlock;
273 274 275
	if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size)
		goto out_unlock;

276
	error = setattr_prepare(dentry, attr);
277 278 279
	if (error)
		goto out_unlock;

Christoph Hellwig's avatar
Christoph Hellwig committed
280 281
	if ((attr->ia_valid & ATTR_SIZE) &&
	    attr->ia_size != i_size_read(inode)) {
Marco Stornelli's avatar
Marco Stornelli committed
282
		error = inode_newsize_ok(inode, attr->ia_size);
Christoph Hellwig's avatar
Christoph Hellwig committed
283
		if (error)
284
			goto out_unlock;
Marco Stornelli's avatar
Marco Stornelli committed
285 286 287

		truncate_setsize(inode, attr->ia_size);
		hpfs_truncate(inode);
Christoph Hellwig's avatar
Christoph Hellwig committed
288 289 290
	}

	setattr_copy(inode, attr);
291 292 293 294

	hpfs_write_inode(inode);

 out_unlock:
Arnd Bergmann's avatar
Arnd Bergmann committed
295
	hpfs_unlock(inode->i_sb);
Linus Torvalds's avatar
Linus Torvalds committed
296 297 298 299 300 301 302 303 304 305 306
	return error;
}

void hpfs_write_if_changed(struct inode *inode)
{
	struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);

	if (hpfs_inode->i_dirty)
		hpfs_write_inode(inode);
}

Al Viro's avatar
Al Viro committed
307
void hpfs_evict_inode(struct inode *inode)
Linus Torvalds's avatar
Linus Torvalds committed
308
{
309
	truncate_inode_pages_final(&inode->i_data);
310
	clear_inode(inode);
Al Viro's avatar
Al Viro committed
311
	if (!inode->i_nlink) {
Arnd Bergmann's avatar
Arnd Bergmann committed
312
		hpfs_lock(inode->i_sb);
Al Viro's avatar
Al Viro committed
313
		hpfs_remove_fnode(inode->i_sb, inode->i_ino);
Arnd Bergmann's avatar
Arnd Bergmann committed
314
		hpfs_unlock(inode->i_sb);
Al Viro's avatar
Al Viro committed
315
	}
Linus Torvalds's avatar
Linus Torvalds committed
316
}