inode.c 9.5 KB
Newer Older
Miklos Szeredi's avatar
Miklos Szeredi committed
1 2 3 4 5 6 7 8 9 10 11 12
/*
 *
 * Copyright (C) 2011 Novell Inc.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */

#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/xattr.h>
13
#include <linux/posix_acl.h>
Miklos Szeredi's avatar
Miklos Szeredi committed
14 15
#include "overlayfs.h"

16
static int ovl_copy_up_truncate(struct dentry *dentry)
Miklos Szeredi's avatar
Miklos Szeredi committed
17 18 19 20 21
{
	int err;
	struct dentry *parent;
	struct kstat stat;
	struct path lowerpath;
22
	const struct cred *old_cred;
Miklos Szeredi's avatar
Miklos Szeredi committed
23 24 25 26 27 28 29 30

	parent = dget_parent(dentry);
	err = ovl_copy_up(parent);
	if (err)
		goto out_dput_parent;

	ovl_path_lower(dentry, &lowerpath);

31 32 33 34 35 36 37
	old_cred = ovl_override_creds(dentry->d_sb);
	err = vfs_getattr(&lowerpath, &stat);
	if (!err) {
		stat.size = 0;
		err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
	}
	revert_creds(old_cred);
Miklos Szeredi's avatar
Miklos Szeredi committed
38 39 40 41 42 43 44 45 46 47

out_dput_parent:
	dput(parent);
	return err;
}

int ovl_setattr(struct dentry *dentry, struct iattr *attr)
{
	int err;
	struct dentry *upperdentry;
48
	const struct cred *old_cred;
Miklos Szeredi's avatar
Miklos Szeredi committed
49

50 51 52 53 54 55 56 57 58
	/*
	 * Check for permissions before trying to copy-up.  This is redundant
	 * since it will be rechecked later by ->setattr() on upper dentry.  But
	 * without this, copy-up can be triggered by just about anybody.
	 *
	 * We don't initialize inode->size, which just means that
	 * inode_newsize_ok() will always check against MAX_LFS_FILESIZE and not
	 * check for a swapfile (which this won't be anyway).
	 */
59
	err = setattr_prepare(dentry, attr);
60 61 62
	if (err)
		return err;

Miklos Szeredi's avatar
Miklos Szeredi committed
63 64 65 66
	err = ovl_want_write(dentry);
	if (err)
		goto out;

67 68 69 70 71 72 73 74
	if (attr->ia_valid & ATTR_SIZE) {
		struct inode *realinode = d_inode(ovl_dentry_real(dentry));

		err = -ETXTBSY;
		if (atomic_read(&realinode->i_writecount) < 0)
			goto out_drop_write;
	}

75 76
	err = ovl_copy_up(dentry);
	if (!err) {
77 78
		struct inode *winode = NULL;

79 80
		upperdentry = ovl_dentry_upper(dentry);

81 82 83 84 85 86 87
		if (attr->ia_valid & ATTR_SIZE) {
			winode = d_inode(upperdentry);
			err = get_write_access(winode);
			if (err)
				goto out_drop_write;
		}

Miklos Szeredi's avatar
Miklos Szeredi committed
88 89 90
		if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
			attr->ia_valid &= ~ATTR_MODE;

Al Viro's avatar
Al Viro committed
91
		inode_lock(upperdentry->d_inode);
92
		old_cred = ovl_override_creds(dentry->d_sb);
Miklos Szeredi's avatar
Miklos Szeredi committed
93
		err = notify_change(upperdentry, attr, NULL);
94
		revert_creds(old_cred);
95 96
		if (!err)
			ovl_copyattr(upperdentry->d_inode, dentry->d_inode);
Al Viro's avatar
Al Viro committed
97
		inode_unlock(upperdentry->d_inode);
98 99 100

		if (winode)
			put_write_access(winode);
Miklos Szeredi's avatar
Miklos Szeredi committed
101
	}
102
out_drop_write:
Miklos Szeredi's avatar
Miklos Szeredi committed
103 104 105 106 107 108 109 110 111
	ovl_drop_write(dentry);
out:
	return err;
}

static int ovl_getattr(struct vfsmount *mnt, struct dentry *dentry,
			 struct kstat *stat)
{
	struct path realpath;
112 113
	const struct cred *old_cred;
	int err;
Miklos Szeredi's avatar
Miklos Szeredi committed
114 115

	ovl_path_real(dentry, &realpath);
116 117 118 119
	old_cred = ovl_override_creds(dentry->d_sb);
	err = vfs_getattr(&realpath, stat);
	revert_creds(old_cred);
	return err;
Miklos Szeredi's avatar
Miklos Szeredi committed
120 121 122 123 124
}

int ovl_permission(struct inode *inode, int mask)
{
	bool is_upper;
125
	struct inode *realinode = ovl_inode_real(inode, &is_upper);
126
	const struct cred *old_cred;
Miklos Szeredi's avatar
Miklos Szeredi committed
127 128 129 130 131
	int err;

	/* Careful in RCU walk mode */
	if (!realinode) {
		WARN_ON(!(mask & MAY_NOT_BLOCK));
132
		return -ECHILD;
Miklos Szeredi's avatar
Miklos Szeredi committed
133 134
	}

135 136 137 138 139 140 141 142 143
	/*
	 * Check overlay inode with the creds of task and underlying inode
	 * with creds of mounter
	 */
	err = generic_permission(inode, mask);
	if (err)
		return err;

	old_cred = ovl_override_creds(inode->i_sb);
144
	if (!is_upper && !special_file(realinode->i_mode) && mask & MAY_WRITE) {
145
		mask &= ~(MAY_WRITE | MAY_APPEND);
146 147 148
		/* Make sure mounter can read file for copy up later */
		mask |= MAY_READ;
	}
149
	err = inode_permission(realinode, mask);
150 151 152
	revert_creds(old_cred);

	return err;
Miklos Szeredi's avatar
Miklos Szeredi committed
153 154
}

155
static const char *ovl_get_link(struct dentry *dentry,
156 157
				struct inode *inode,
				struct delayed_call *done)
Miklos Szeredi's avatar
Miklos Szeredi committed
158
{
159 160
	const struct cred *old_cred;
	const char *p;
Miklos Szeredi's avatar
Miklos Szeredi committed
161

162 163 164
	if (!dentry)
		return ERR_PTR(-ECHILD);

165
	old_cred = ovl_override_creds(dentry->d_sb);
Miklos Szeredi's avatar
Miklos Szeredi committed
166
	p = vfs_get_link(ovl_dentry_real(dentry), done);
167 168
	revert_creds(old_cred);
	return p;
Miklos Szeredi's avatar
Miklos Szeredi committed
169 170
}

171
bool ovl_is_private_xattr(const char *name)
Miklos Szeredi's avatar
Miklos Szeredi committed
172
{
173 174
	return strncmp(name, OVL_XATTR_PREFIX,
		       sizeof(OVL_XATTR_PREFIX) - 1) == 0;
Miklos Szeredi's avatar
Miklos Szeredi committed
175 176
}

177 178
int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value,
		  size_t size, int flags)
Miklos Szeredi's avatar
Miklos Szeredi committed
179 180
{
	int err;
181 182
	struct path realpath;
	enum ovl_path_type type = ovl_path_real(dentry, &realpath);
183
	const struct cred *old_cred;
Miklos Szeredi's avatar
Miklos Szeredi committed
184 185 186 187 188

	err = ovl_want_write(dentry);
	if (err)
		goto out;

189 190 191 192 193 194
	if (!value && !OVL_TYPE_UPPER(type)) {
		err = vfs_getxattr(realpath.dentry, name, NULL, 0);
		if (err < 0)
			goto out_drop_write;
	}

Miklos Szeredi's avatar
Miklos Szeredi committed
195 196 197 198
	err = ovl_copy_up(dentry);
	if (err)
		goto out_drop_write;

199 200 201
	if (!OVL_TYPE_UPPER(type))
		ovl_path_upper(dentry, &realpath);

202
	old_cred = ovl_override_creds(dentry->d_sb);
203 204 205 206 207 208
	if (value)
		err = vfs_setxattr(realpath.dentry, name, value, size, flags);
	else {
		WARN_ON(flags != XATTR_REPLACE);
		err = vfs_removexattr(realpath.dentry, name);
	}
209
	revert_creds(old_cred);
Miklos Szeredi's avatar
Miklos Szeredi committed
210 211 212 213 214 215 216

out_drop_write:
	ovl_drop_write(dentry);
out:
	return err;
}

217 218
int ovl_xattr_get(struct dentry *dentry, const char *name,
		  void *value, size_t size)
Miklos Szeredi's avatar
Miklos Szeredi committed
219
{
Miklos Szeredi's avatar
Miklos Szeredi committed
220
	struct dentry *realdentry = ovl_dentry_real(dentry);
221 222
	ssize_t res;
	const struct cred *old_cred;
223

224 225 226 227
	old_cred = ovl_override_creds(dentry->d_sb);
	res = vfs_getxattr(realdentry, name, value, size);
	revert_creds(old_cred);
	return res;
Miklos Szeredi's avatar
Miklos Szeredi committed
228 229
}

230 231 232 233 234 235 236 237 238 239
static bool ovl_can_list(const char *s)
{
	/* List all non-trusted xatts */
	if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
		return true;

	/* Never list trusted.overlay, list other trusted for superuser only */
	return !ovl_is_private_xattr(s) && capable(CAP_SYS_ADMIN);
}

Miklos Szeredi's avatar
Miklos Szeredi committed
240 241
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{
Miklos Szeredi's avatar
Miklos Szeredi committed
242
	struct dentry *realdentry = ovl_dentry_real(dentry);
Miklos Szeredi's avatar
Miklos Szeredi committed
243
	ssize_t res;
244 245
	size_t len;
	char *s;
246
	const struct cred *old_cred;
Miklos Szeredi's avatar
Miklos Szeredi committed
247

248
	old_cred = ovl_override_creds(dentry->d_sb);
Miklos Szeredi's avatar
Miklos Szeredi committed
249
	res = vfs_listxattr(realdentry, list, size);
250
	revert_creds(old_cred);
Miklos Szeredi's avatar
Miklos Szeredi committed
251 252 253 254
	if (res <= 0 || size == 0)
		return res;

	/* filter out private xattrs */
255 256
	for (s = list, len = res; len;) {
		size_t slen = strnlen(s, len) + 1;
Miklos Szeredi's avatar
Miklos Szeredi committed
257

258 259 260
		/* underlying fs providing us with an broken xattr list? */
		if (WARN_ON(slen > len))
			return -EIO;
Miklos Szeredi's avatar
Miklos Szeredi committed
261

262
		len -= slen;
263
		if (!ovl_can_list(s)) {
Miklos Szeredi's avatar
Miklos Szeredi committed
264
			res -= slen;
265
			memmove(s, s + slen, len);
Miklos Szeredi's avatar
Miklos Szeredi committed
266
		} else {
267
			s += slen;
Miklos Szeredi's avatar
Miklos Szeredi committed
268 269 270 271 272 273
		}
	}

	return res;
}

274 275
struct posix_acl *ovl_get_acl(struct inode *inode, int type)
{
276
	struct inode *realinode = ovl_inode_real(inode, NULL);
277 278
	const struct cred *old_cred;
	struct posix_acl *acl;
279

280
	if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
281 282
		return NULL;

283
	old_cred = ovl_override_creds(inode->i_sb);
284
	acl = get_acl(realinode, type);
285 286 287
	revert_creds(old_cred);

	return acl;
288 289
}

Miklos Szeredi's avatar
Miklos Szeredi committed
290 291 292
static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
				  struct dentry *realdentry)
{
293
	if (OVL_TYPE_UPPER(type))
Miklos Szeredi's avatar
Miklos Szeredi committed
294 295 296 297 298 299 300 301 302 303 304
		return false;

	if (special_file(realdentry->d_inode->i_mode))
		return false;

	if (!(OPEN_FMODE(flags) & FMODE_WRITE) && !(flags & O_TRUNC))
		return false;

	return true;
}

305
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
Miklos Szeredi's avatar
Miklos Szeredi committed
306
{
307
	int err = 0;
Miklos Szeredi's avatar
Miklos Szeredi committed
308 309 310 311
	struct path realpath;
	enum ovl_path_type type;

	type = ovl_path_real(dentry, &realpath);
312
	if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
Miklos Szeredi's avatar
Miklos Szeredi committed
313
		err = ovl_want_write(dentry);
314 315 316 317 318 319 320
		if (!err) {
			if (file_flags & O_TRUNC)
				err = ovl_copy_up_truncate(dentry);
			else
				err = ovl_copy_up(dentry);
			ovl_drop_write(dentry);
		}
Miklos Szeredi's avatar
Miklos Szeredi committed
321 322
	}

323
	return err;
Miklos Szeredi's avatar
Miklos Szeredi committed
324 325
}

Miklos Szeredi's avatar
Miklos Szeredi committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
int ovl_update_time(struct inode *inode, struct timespec *ts, int flags)
{
	struct dentry *alias;
	struct path upperpath;

	if (!(flags & S_ATIME))
		return 0;

	alias = d_find_any_alias(inode);
	if (!alias)
		return 0;

	ovl_path_upper(alias, &upperpath);
	if (upperpath.dentry) {
		touch_atime(&upperpath);
		inode->i_atime = d_inode(upperpath.dentry)->i_atime;
	}

	dput(alias);

	return 0;
}

Miklos Szeredi's avatar
Miklos Szeredi committed
349 350 351 352 353
static const struct inode_operations ovl_file_inode_operations = {
	.setattr	= ovl_setattr,
	.permission	= ovl_permission,
	.getattr	= ovl_getattr,
	.listxattr	= ovl_listxattr,
354
	.get_acl	= ovl_get_acl,
Miklos Szeredi's avatar
Miklos Szeredi committed
355
	.update_time	= ovl_update_time,
Miklos Szeredi's avatar
Miklos Szeredi committed
356 357 358 359
};

static const struct inode_operations ovl_symlink_inode_operations = {
	.setattr	= ovl_setattr,
360
	.get_link	= ovl_get_link,
Miklos Szeredi's avatar
Miklos Szeredi committed
361
	.readlink	= generic_readlink,
Miklos Szeredi's avatar
Miklos Szeredi committed
362 363
	.getattr	= ovl_getattr,
	.listxattr	= ovl_listxattr,
Miklos Szeredi's avatar
Miklos Szeredi committed
364
	.update_time	= ovl_update_time,
Miklos Szeredi's avatar
Miklos Szeredi committed
365 366
};

367
static void ovl_fill_inode(struct inode *inode, umode_t mode)
Miklos Szeredi's avatar
Miklos Szeredi committed
368 369 370
{
	inode->i_ino = get_next_ino();
	inode->i_mode = mode;
Miklos Szeredi's avatar
Miklos Szeredi committed
371
	inode->i_flags |= S_NOCMTIME;
372 373 374
#ifdef CONFIG_FS_POSIX_ACL
	inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
#endif
Miklos Szeredi's avatar
Miklos Szeredi committed
375

376
	mode &= S_IFMT;
Miklos Szeredi's avatar
Miklos Szeredi committed
377 378 379 380 381 382 383 384 385 386
	switch (mode) {
	case S_IFDIR:
		inode->i_op = &ovl_dir_inode_operations;
		inode->i_fop = &ovl_dir_operations;
		break;

	case S_IFLNK:
		inode->i_op = &ovl_symlink_inode_operations;
		break;

387 388 389 390
	default:
		WARN(1, "illegal file type: %i\n", mode);
		/* Fall through */

Miklos Szeredi's avatar
Miklos Szeredi committed
391 392 393 394 395 396 397
	case S_IFREG:
	case S_IFSOCK:
	case S_IFBLK:
	case S_IFCHR:
	case S_IFIFO:
		inode->i_op = &ovl_file_inode_operations;
		break;
398 399
	}
}
Miklos Szeredi's avatar
Miklos Szeredi committed
400

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode)
{
	struct inode *inode;

	inode = new_inode(sb);
	if (inode)
		ovl_fill_inode(inode, mode);

	return inode;
}

static int ovl_inode_test(struct inode *inode, void *data)
{
	return ovl_inode_real(inode, NULL) == data;
}

static int ovl_inode_set(struct inode *inode, void *data)
{
	inode->i_private = (void *) (((unsigned long) data) | OVL_ISUPPER_MASK);
	return 0;
}

struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode)

{
	struct inode *inode;

	inode = iget5_locked(sb, (unsigned long) realinode,
			     ovl_inode_test, ovl_inode_set, realinode);
	if (inode && inode->i_state & I_NEW) {
		ovl_fill_inode(inode, realinode->i_mode);
		set_nlink(inode, realinode->i_nlink);
		unlock_new_inode(inode);
Miklos Szeredi's avatar
Miklos Szeredi committed
434 435 436 437
	}

	return inode;
}