Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
4bb0fb5
Documentation
arch
block
certs
crypto
drivers
firmware
fs
9p
adfs
affs
afs
autofs4
befs
bfs
btrfs
cachefiles
ceph
cifs
coda
configfs
cramfs
debugfs
devpts
dlm
ecryptfs
efivarfs
efs
exofs
exportfs
ext2
ext4
f2fs
fat
freevxfs
fscache
fuse
gfs2
hfs
hfsplus
hostfs
hpfs
hugetlbfs
isofs
jbd2
jffs2
jfs
kernfs
lockd
logfs
minix
ncpfs
nfs
nfs_common
nfsd
nilfs2
nls
notify
ntfs
ocfs2
omfs
openpromfs
overlayfs
Kconfig
Makefile
copy_up.c
dir.c
inode.c
overlayfs.h
readdir.c
super.c
proc
pstore
qnx4
qnx6
quota
ramfs
reiserfs
romfs
squashfs
sysfs
sysv
tracefs
ubifs
udf
ufs
xfs
Kconfig
Kconfig.binfmt
Makefile
aio.c
anon_inodes.c
attr.c
bad_inode.c
binfmt_aout.c
binfmt_elf.c
binfmt_elf_fdpic.c
binfmt_em86.c
binfmt_flat.c
binfmt_misc.c
binfmt_script.c
block_dev.c
buffer.c
char_dev.c
compat.c
compat_binfmt_elf.c
compat_ioctl.c
coredump.c
dax.c
dcache.c
dcookies.c
direct-io.c
drop_caches.c
eventfd.c
eventpoll.c
exec.c
fcntl.c
fhandle.c
file.c
file_table.c
filesystems.c
fs-writeback.c
fs_pin.c
fs_struct.c
inode.c
internal.h
ioctl.c
libfs.c
locks.c
mbcache.c
mount.h
mpage.c
namei.c
namespace.c
no-block.c
nsfs.c
open.c
pipe.c
pnode.c
pnode.h
posix_acl.c
proc_namespace.c
read_write.c
readdir.c
select.c
seq_file.c
signalfd.c
splice.c
stack.c
stat.c
statfs.c
super.c
sync.c
timerfd.c
userfaultfd.c
utimes.c
xattr.c
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.get_maintainer.ignore
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
fs
/
overlayfs
/
inode.c
Blame
Blame
Latest commit
History
History
435 lines (349 loc) · 9 KB
Breadcrumbs
linux
/
fs
/
overlayfs
/
inode.c
Top
File metadata and controls
Code
Blame
435 lines (349 loc) · 9 KB
Raw
/* * * 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 const char *ovl_follow_link(struct dentry *dentry, void **cookie) { struct dentry *realdentry; struct inode *realinode; struct ovl_link_data *data = NULL; const char *ret; realdentry = ovl_dentry_real(dentry); realinode = realdentry->d_inode; if (WARN_ON(!realinode->i_op->follow_link)) return ERR_PTR(-EPERM); if (realinode->i_op->put_link) { data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL); if (!data) return ERR_PTR(-ENOMEM); data->realdentry = realdentry; } ret = realinode->i_op->follow_link(realdentry, cookie); if (IS_ERR_OR_NULL(ret)) { kfree(data); return ret; } if (data) data->cookie = *cookie; *cookie = data; return ret; } static void ovl_put_link(struct inode *unused, void *c) { struct inode *realinode; struct ovl_link_data *data = c; if (!data) return; realinode = data->realdentry->d_inode; realinode->i_op->put_link(realinode, 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) { return strncmp(name, OVL_XATTR_PRE_NAME, OVL_XATTR_PRE_LEN) == 0; } 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; } static bool ovl_need_xattr_filter(struct dentry *dentry, enum ovl_path_type type) { if ((type & (__OVL_PATH_PURE | __OVL_PATH_UPPER)) == __OVL_PATH_UPPER) return S_ISDIR(dentry->d_inode->i_mode); else return false; } ssize_t ovl_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { 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)) return -ENODATA; return vfs_getxattr(realpath.dentry, name, value, size); } ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) { struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); ssize_t res; int off; res = vfs_listxattr(realpath.dentry, list, size); if (res <= 0 || size == 0) return res; if (!ovl_need_xattr_filter(dentry, type)) 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; enum ovl_path_type type = ovl_path_real(dentry, &realpath); err = ovl_want_write(dentry); if (err) goto out; err = -ENODATA; if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name)) goto out_drop_write; if (!OVL_TYPE_UPPER(type)) { 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) { if (OVL_TYPE_UPPER(type)) 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; } struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags) { int err; struct path realpath; enum ovl_path_type type; if (d_is_dir(dentry)) return d_backing_inode(dentry); type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) { err = ovl_want_write(dentry); if (err) return ERR_PTR(err); if (file_flags & O_TRUNC) err = ovl_copy_up_last(dentry, NULL, true); else err = ovl_copy_up(dentry); ovl_drop_write(dentry); if (err) return ERR_PTR(err); ovl_path_upper(dentry, &realpath); } if (realpath.dentry->d_flags & DCACHE_OP_SELECT_INODE) return realpath.dentry->d_op->d_select_inode(realpath.dentry, file_flags); return d_backing_inode(realpath.dentry); } 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, }; 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\n", mode); iput(inode); inode = NULL; } return inode; }
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
362
363
364
365
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
422
423
424
425
426
427
428
429
430
431
432
433
434
435
You can’t perform that action at this time.