Blame view

fs/overlayfs/inode.c 8.91 KB
e9be9d5e7   Miklos Szeredi   overlay filesystem
1
2
3
4
5
6
7
8
9
10
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  /*
   *
   * 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>
  #include "overlayfs.h"
  
  static int ovl_copy_up_last(struct dentry *dentry, struct iattr *attr,
  			    bool no_data)
  {
  	int err;
  	struct dentry *parent;
  	struct kstat stat;
  	struct path lowerpath;
  
  	parent = dget_parent(dentry);
  	err = ovl_copy_up(parent);
  	if (err)
  		goto out_dput_parent;
  
  	ovl_path_lower(dentry, &lowerpath);
  	err = vfs_getattr(&lowerpath, &stat);
  	if (err)
  		goto out_dput_parent;
  
  	if (no_data)
  		stat.size = 0;
  
  	err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat, attr);
  
  out_dput_parent:
  	dput(parent);
  	return err;
  }
  
  int ovl_setattr(struct dentry *dentry, struct iattr *attr)
  {
  	int err;
  	struct dentry *upperdentry;
  
  	err = ovl_want_write(dentry);
  	if (err)
  		goto out;
  
  	upperdentry = ovl_dentry_upper(dentry);
  	if (upperdentry) {
  		mutex_lock(&upperdentry->d_inode->i_mutex);
  		err = notify_change(upperdentry, attr, NULL);
  		mutex_unlock(&upperdentry->d_inode->i_mutex);
  	} else {
  		err = ovl_copy_up_last(dentry, attr, false);
  	}
  	ovl_drop_write(dentry);
  out:
  	return err;
  }
  
  static int ovl_getattr(struct vfsmount *mnt, struct dentry *dentry,
  			 struct kstat *stat)
  {
  	struct path realpath;
  
  	ovl_path_real(dentry, &realpath);
  	return vfs_getattr(&realpath, stat);
  }
  
  int ovl_permission(struct inode *inode, int mask)
  {
  	struct ovl_entry *oe;
  	struct dentry *alias = NULL;
  	struct inode *realinode;
  	struct dentry *realdentry;
  	bool is_upper;
  	int err;
  
  	if (S_ISDIR(inode->i_mode)) {
  		oe = inode->i_private;
  	} else if (mask & MAY_NOT_BLOCK) {
  		return -ECHILD;
  	} else {
  		/*
  		 * For non-directories find an alias and get the info
  		 * from there.
  		 */
  		alias = d_find_any_alias(inode);
  		if (WARN_ON(!alias))
  			return -ENOENT;
  
  		oe = alias->d_fsdata;
  	}
  
  	realdentry = ovl_entry_real(oe, &is_upper);
  
  	/* Careful in RCU walk mode */
  	realinode = ACCESS_ONCE(realdentry->d_inode);
  	if (!realinode) {
  		WARN_ON(!(mask & MAY_NOT_BLOCK));
  		err = -ENOENT;
  		goto out_dput;
  	}
  
  	if (mask & MAY_WRITE) {
  		umode_t mode = realinode->i_mode;
  
  		/*
  		 * Writes will always be redirected to upper layer, so
  		 * ignore lower layer being read-only.
  		 *
  		 * If the overlay itself is read-only then proceed
  		 * with the permission check, don't return EROFS.
  		 * This will only happen if this is the lower layer of
  		 * another overlayfs.
  		 *
  		 * If upper fs becomes read-only after the overlay was
  		 * constructed return EROFS to prevent modification of
  		 * upper layer.
  		 */
  		err = -EROFS;
  		if (is_upper && !IS_RDONLY(inode) && IS_RDONLY(realinode) &&
  		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
  			goto out_dput;
  	}
  
  	err = __inode_permission(realinode, mask);
  out_dput:
  	dput(alias);
  	return err;
  }
  
  
  struct ovl_link_data {
  	struct dentry *realdentry;
  	void *cookie;
  };
  
  static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd)
  {
  	void *ret;
  	struct dentry *realdentry;
  	struct inode *realinode;
  
  	realdentry = ovl_dentry_real(dentry);
  	realinode = realdentry->d_inode;
  
  	if (WARN_ON(!realinode->i_op->follow_link))
  		return ERR_PTR(-EPERM);
  
  	ret = realinode->i_op->follow_link(realdentry, nd);
  	if (IS_ERR(ret))
  		return ret;
  
  	if (realinode->i_op->put_link) {
  		struct ovl_link_data *data;
  
  		data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL);
  		if (!data) {
  			realinode->i_op->put_link(realdentry, nd, ret);
  			return ERR_PTR(-ENOMEM);
  		}
  		data->realdentry = realdentry;
  		data->cookie = ret;
  
  		return data;
  	} else {
  		return NULL;
  	}
  }
  
  static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
  {
  	struct inode *realinode;
  	struct ovl_link_data *data = c;
  
  	if (!data)
  		return;
  
  	realinode = data->realdentry->d_inode;
  	realinode->i_op->put_link(data->realdentry, nd, data->cookie);
  	kfree(data);
  }
  
  static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
  {
  	struct path realpath;
  	struct inode *realinode;
  
  	ovl_path_real(dentry, &realpath);
  	realinode = realpath.dentry->d_inode;
  
  	if (!realinode->i_op->readlink)
  		return -EINVAL;
  
  	touch_atime(&realpath);
  
  	return realinode->i_op->readlink(realpath.dentry, buf, bufsiz);
  }
  
  
  static bool ovl_is_private_xattr(const char *name)
  {
cead89bb0   hujianyang   ovl: Use macros t...
208
  	return strncmp(name, OVL_XATTR_PRE_NAME, OVL_XATTR_PRE_LEN) == 0;
e9be9d5e7   Miklos Szeredi   overlay filesystem
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  }
  
  int ovl_setxattr(struct dentry *dentry, const char *name,
  		 const void *value, size_t size, int flags)
  {
  	int err;
  	struct dentry *upperdentry;
  
  	err = ovl_want_write(dentry);
  	if (err)
  		goto out;
  
  	err = -EPERM;
  	if (ovl_is_private_xattr(name))
  		goto out_drop_write;
  
  	err = ovl_copy_up(dentry);
  	if (err)
  		goto out_drop_write;
  
  	upperdentry = ovl_dentry_upper(dentry);
  	err = vfs_setxattr(upperdentry, name, value, size, flags);
  
  out_drop_write:
  	ovl_drop_write(dentry);
  out:
  	return err;
  }
521484639   Miklos Szeredi   ovl: fix race in ...
237
238
239
  static bool ovl_need_xattr_filter(struct dentry *dentry,
  				  enum ovl_path_type type)
  {
1afaba1ec   Miklos Szeredi   ovl: make path-ty...
240
241
242
243
  	if ((type & (__OVL_PATH_PURE | __OVL_PATH_UPPER)) == __OVL_PATH_UPPER)
  		return S_ISDIR(dentry->d_inode->i_mode);
  	else
  		return false;
521484639   Miklos Szeredi   ovl: fix race in ...
244
  }
e9be9d5e7   Miklos Szeredi   overlay filesystem
245
246
247
  ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
  		     void *value, size_t size)
  {
521484639   Miklos Szeredi   ovl: fix race in ...
248
249
250
251
  	struct path realpath;
  	enum ovl_path_type type = ovl_path_real(dentry, &realpath);
  
  	if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
e9be9d5e7   Miklos Szeredi   overlay filesystem
252
  		return -ENODATA;
521484639   Miklos Szeredi   ovl: fix race in ...
253
  	return vfs_getxattr(realpath.dentry, name, value, size);
e9be9d5e7   Miklos Szeredi   overlay filesystem
254
255
256
257
  }
  
  ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
  {
521484639   Miklos Szeredi   ovl: fix race in ...
258
259
  	struct path realpath;
  	enum ovl_path_type type = ovl_path_real(dentry, &realpath);
e9be9d5e7   Miklos Szeredi   overlay filesystem
260
261
  	ssize_t res;
  	int off;
521484639   Miklos Szeredi   ovl: fix race in ...
262
  	res = vfs_listxattr(realpath.dentry, list, size);
e9be9d5e7   Miklos Szeredi   overlay filesystem
263
264
  	if (res <= 0 || size == 0)
  		return res;
521484639   Miklos Szeredi   ovl: fix race in ...
265
  	if (!ovl_need_xattr_filter(dentry, type))
e9be9d5e7   Miklos Szeredi   overlay filesystem
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  		return res;
  
  	/* filter out private xattrs */
  	for (off = 0; off < res;) {
  		char *s = list + off;
  		size_t slen = strlen(s) + 1;
  
  		BUG_ON(off + slen > res);
  
  		if (ovl_is_private_xattr(s)) {
  			res -= slen;
  			memmove(s, s + slen, res - off);
  		} else {
  			off += slen;
  		}
  	}
  
  	return res;
  }
  
  int ovl_removexattr(struct dentry *dentry, const char *name)
  {
  	int err;
  	struct path realpath;
521484639   Miklos Szeredi   ovl: fix race in ...
290
  	enum ovl_path_type type = ovl_path_real(dentry, &realpath);
e9be9d5e7   Miklos Szeredi   overlay filesystem
291
292
293
294
  
  	err = ovl_want_write(dentry);
  	if (err)
  		goto out;
521484639   Miklos Szeredi   ovl: fix race in ...
295
296
  	err = -ENODATA;
  	if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
e9be9d5e7   Miklos Szeredi   overlay filesystem
297
  		goto out_drop_write;
1afaba1ec   Miklos Szeredi   ovl: make path-ty...
298
  	if (!OVL_TYPE_UPPER(type)) {
e9be9d5e7   Miklos Szeredi   overlay filesystem
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  		err = vfs_getxattr(realpath.dentry, name, NULL, 0);
  		if (err < 0)
  			goto out_drop_write;
  
  		err = ovl_copy_up(dentry);
  		if (err)
  			goto out_drop_write;
  
  		ovl_path_upper(dentry, &realpath);
  	}
  
  	err = vfs_removexattr(realpath.dentry, name);
  out_drop_write:
  	ovl_drop_write(dentry);
  out:
  	return err;
  }
  
  static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
  				  struct dentry *realdentry)
  {
1afaba1ec   Miklos Szeredi   ovl: make path-ty...
320
  	if (OVL_TYPE_UPPER(type))
e9be9d5e7   Miklos Szeredi   overlay filesystem
321
322
323
324
325
326
327
328
329
330
  		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;
  }
9abb3b810   David Howells   overlayfs: Make f...
331
  struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags)
e9be9d5e7   Miklos Szeredi   overlay filesystem
332
333
334
335
  {
  	int err;
  	struct path realpath;
  	enum ovl_path_type type;
e9be9d5e7   Miklos Szeredi   overlay filesystem
336

aaf19f122   Al Viro   fix a braino in o...
337
338
  	if (d_is_dir(dentry))
  		return d_backing_inode(dentry);
e9be9d5e7   Miklos Szeredi   overlay filesystem
339
  	type = ovl_path_real(dentry, &realpath);
9abb3b810   David Howells   overlayfs: Make f...
340
  	if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
e9be9d5e7   Miklos Szeredi   overlay filesystem
341
342
  		err = ovl_want_write(dentry);
  		if (err)
9abb3b810   David Howells   overlayfs: Make f...
343
  			return ERR_PTR(err);
e9be9d5e7   Miklos Szeredi   overlay filesystem
344

9abb3b810   David Howells   overlayfs: Make f...
345
  		if (file_flags & O_TRUNC)
e9be9d5e7   Miklos Szeredi   overlay filesystem
346
347
348
  			err = ovl_copy_up_last(dentry, NULL, true);
  		else
  			err = ovl_copy_up(dentry);
0d2ea357d   David Howells   overlay: Call ovl...
349
  		ovl_drop_write(dentry);
e9be9d5e7   Miklos Szeredi   overlay filesystem
350
  		if (err)
9abb3b810   David Howells   overlayfs: Make f...
351
  			return ERR_PTR(err);
e9be9d5e7   Miklos Szeredi   overlay filesystem
352
353
354
  
  		ovl_path_upper(dentry, &realpath);
  	}
9abb3b810   David Howells   overlayfs: Make f...
355
  	return d_backing_inode(realpath.dentry);
e9be9d5e7   Miklos Szeredi   overlay filesystem
356
357
358
359
360
361
362
363
364
365
  }
  
  static const struct inode_operations ovl_file_inode_operations = {
  	.setattr	= ovl_setattr,
  	.permission	= ovl_permission,
  	.getattr	= ovl_getattr,
  	.setxattr	= ovl_setxattr,
  	.getxattr	= ovl_getxattr,
  	.listxattr	= ovl_listxattr,
  	.removexattr	= ovl_removexattr,
e9be9d5e7   Miklos Szeredi   overlay filesystem
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
  };
  
  static const struct inode_operations ovl_symlink_inode_operations = {
  	.setattr	= ovl_setattr,
  	.follow_link	= ovl_follow_link,
  	.put_link	= ovl_put_link,
  	.readlink	= ovl_readlink,
  	.getattr	= ovl_getattr,
  	.setxattr	= ovl_setxattr,
  	.getxattr	= ovl_getxattr,
  	.listxattr	= ovl_listxattr,
  	.removexattr	= ovl_removexattr,
  };
  
  struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
  			    struct ovl_entry *oe)
  {
  	struct inode *inode;
  
  	inode = new_inode(sb);
  	if (!inode)
  		return NULL;
  
  	mode &= S_IFMT;
  
  	inode->i_ino = get_next_ino();
  	inode->i_mode = mode;
  	inode->i_flags |= S_NOATIME | S_NOCMTIME;
  
  	switch (mode) {
  	case S_IFDIR:
  		inode->i_private = oe;
  		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;
  
  	case S_IFREG:
  	case S_IFSOCK:
  	case S_IFBLK:
  	case S_IFCHR:
  	case S_IFIFO:
  		inode->i_op = &ovl_file_inode_operations;
  		break;
  
  	default:
  		WARN(1, "illegal file type: %i
  ", mode);
  		iput(inode);
  		inode = NULL;
  	}
  
  	return inode;
e9be9d5e7   Miklos Szeredi   overlay filesystem
422
  }