Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671// SPDX-License-Identifier: GPL-2.0-only/* * linux/fs/open.c * * Copyright (C) 1991, 1992 Linus Torvalds */
#include <linux/string.h>#include <linux/mm.h>#include <linux/file.h>#include <linux/fdtable.h>#include <linux/fsnotify.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/namei.h>#include <linux/backing-dev.h>#include <linux/capability.h>#include <linux/securebits.h>#include <linux/security.h>#include <linux/mount.h>#include <linux/fcntl.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/fs.h>#include <linux/personality.h>#include <linux/pagemap.h>#include <linux/syscalls.h>#include <linux/rcupdate.h>#include <linux/audit.h>#include <linux/falloc.h>#include <linux/fs_struct.h>#include <linux/dnotify.h>#include <linux/compat.h>#include <linux/mnt_idmapping.h>#include <linux/filelock.h>
#include "internal.h"
int do_truncate(const struct mnt_idmap *idmap, struct dentry *dentry, loff_t length, unsigned int time_attrs, struct file *filp){ int ret; struct iattr newattrs;
/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */ if (length < 0) return -EINVAL;
newattrs.ia_size = length; newattrs.ia_valid = ATTR_SIZE | time_attrs; if (filp) { newattrs.ia_file = filp; newattrs.ia_valid |= ATTR_FILE; }
/* Remove suid, sgid, and file capabilities on truncate too */ ret = dentry_needs_remove_privs(idmap, dentry); if (ret < 0) return ret; if (ret) newattrs.ia_valid |= ret | ATTR_FORCE;
ret = inode_lock_killable(dentry->d_inode); if (ret) return ret;
/* Note any delegations or leases have already been broken: */ ret = notify_change(idmap, dentry, &newattrs, NULL); inode_unlock(dentry->d_inode); return ret;}
int vfs_truncate(const struct path *path, loff_t length){ const struct mnt_idmap *idmap; struct inode *inode; int error;
inode = path->dentry->d_inode;
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */ if (S_ISDIR(inode->i_mode)) return -EISDIR; if (!S_ISREG(inode->i_mode)) return -EINVAL;
idmap = mnt_idmap(path->mnt); error = inode_permission(idmap, inode, MAY_WRITE); if (error) return error;
error = fsnotify_truncate_perm(path, length); if (error) return error;
error = mnt_want_write(path->mnt); if (error) return error;
error = -EPERM; if (IS_APPEND(inode)) goto mnt_drop_write_and_out;
error = get_write_access(inode); if (error) goto mnt_drop_write_and_out;
/* * Make sure that there are no leases. get_write_access() protects * against the truncate racing with a lease-granting setlease(). */ error = break_lease(inode, O_WRONLY); if (error) goto put_write_and_out;
error = security_path_truncate(path); if (!error) error = do_truncate(idmap, path->dentry, length, 0, NULL);
put_write_and_out: put_write_access(inode);mnt_drop_write_and_out: mnt_drop_write(path->mnt);
return error;}EXPORT_SYMBOL_GPL(vfs_truncate);
int ksys_truncate(const char __user *pathname, loff_t length){ unsigned int lookup_flags = LOOKUP_FOLLOW; struct path path; int error;
if (length < 0) /* sorry, but loff_t says... */ return -EINVAL;
CLASS(filename, name)(pathname);retry: error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL); if (!error) { error = vfs_truncate(&path, length); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } } return error;}
SYSCALL_DEFINE2(truncate, const char __user *, path, long, length){ return ksys_truncate(path, length);}
#ifdef CONFIG_COMPATCOMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, length){ return ksys_truncate(path, length);}#endif
int do_ftruncate(struct file *file, loff_t length, unsigned int flags){ struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; int error;
if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE)) return -EINVAL;
/* * Cannot ftruncate over 2^31 bytes without large file support, either * through opening with O_LARGEFILE or by using ftruncate64(). */ if (length > MAX_NON_LFS && !(file->f_flags & O_LARGEFILE) && !(flags & FTRUNCATE_LFS)) return -EINVAL;
/* Check IS_APPEND on real upper inode */ if (IS_APPEND(file_inode(file))) return -EPERM;
error = security_file_truncate(file); if (error) return error;
error = fsnotify_truncate_perm(&file->f_path, length); if (error) return error;
scoped_guard(super_write, inode->i_sb) return do_truncate(file_mnt_idmap(file), dentry, length, ATTR_MTIME | ATTR_CTIME, file);}
int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags){ if (length < 0) return -EINVAL; CLASS(fd, f)(fd); if (fd_empty(f)) return -EBADF;
return do_ftruncate(fd_file(f), length, flags);}
SYSCALL_DEFINE2(ftruncate, unsigned int, fd, off_t, length){ return ksys_ftruncate(fd, length, 0);}
#ifdef CONFIG_COMPATCOMPAT_SYSCALL_DEFINE2(ftruncate, unsigned int, fd, compat_off_t, length){ return ksys_ftruncate(fd, length, 0);}#endif
/* LFS versions of truncate are only needed on 32 bit machines */#if BITS_PER_LONG == 32SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length){ return ksys_truncate(path, length);}
SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length){ return ksys_ftruncate(fd, length, FTRUNCATE_LFS);}#endif /* BITS_PER_LONG == 32 */
#if defined(CONFIG_COMPAT) && defined(__ARCH_WANT_COMPAT_TRUNCATE64)COMPAT_SYSCALL_DEFINE3(truncate64, const char __user *, pathname, compat_arg_u64_dual(length)){ return ksys_truncate(pathname, compat_arg_u64_glue(length));}#endif
#if defined(CONFIG_COMPAT) && defined(__ARCH_WANT_COMPAT_FTRUNCATE64)COMPAT_SYSCALL_DEFINE3(ftruncate64, unsigned int, fd, compat_arg_u64_dual(length)){ return ksys_ftruncate(fd, compat_arg_u64_glue(length), FTRUNCATE_LFS);}#endif
int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len){ struct inode *inode = file_inode(file); int ret; loff_t sum;
if (offset < 0 || len <= 0) return -EINVAL;
if (mode & ~(FALLOC_FL_MODE_MASK | FALLOC_FL_KEEP_SIZE)) return -EOPNOTSUPP;
/* * Modes are exclusive, even if that is not obvious from the encoding * as bit masks and the mix with the flag in the same namespace. * * To make things even more complicated, FALLOC_FL_ALLOCATE_RANGE is * encoded as no bit set. */ switch (mode & FALLOC_FL_MODE_MASK) { case FALLOC_FL_ALLOCATE_RANGE: case FALLOC_FL_UNSHARE_RANGE: case FALLOC_FL_ZERO_RANGE: break; case FALLOC_FL_PUNCH_HOLE: if (!(mode & FALLOC_FL_KEEP_SIZE)) return -EOPNOTSUPP; break; case FALLOC_FL_COLLAPSE_RANGE: case FALLOC_FL_INSERT_RANGE: case FALLOC_FL_WRITE_ZEROES: if (mode & FALLOC_FL_KEEP_SIZE) return -EOPNOTSUPP; break; default: return -EOPNOTSUPP; }
if (!(file->f_mode & FMODE_WRITE)) return -EBADF;
/* * On append-only files only space preallocation is supported. */ if ((mode & ~FALLOC_FL_KEEP_SIZE) && IS_APPEND(inode)) return -EPERM;
if (IS_IMMUTABLE(inode)) return -EPERM;
/* * We cannot allow any fallocate operation on an active swapfile */ if (IS_SWAPFILE(inode)) return -ETXTBSY;
/* * Revalidate the write permissions, in case security policy has * changed since the files were opened. */ ret = security_file_permission(file, MAY_WRITE); if (ret) return ret;
ret = fsnotify_file_area_perm(file, MAY_WRITE, &offset, len); if (ret) return ret;
if (S_ISFIFO(inode->i_mode)) return -ESPIPE;
if (S_ISDIR(inode->i_mode)) return -EISDIR;
if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) return -ENODEV;
/* Check for wraparound */ if (check_add_overflow(offset, len, &sum)) return -EFBIG;
if (sum > inode->i_sb->s_maxbytes) return -EFBIG;
if (!file->f_op->fallocate) return -EOPNOTSUPP;
file_start_write(file); ret = file->f_op->fallocate(file, mode, offset, len);
/* * Create inotify and fanotify events. * * To keep the logic simple always create events if fallocate succeeds. * This implies that events are even created if the file size remains * unchanged, e.g. when using flag FALLOC_FL_KEEP_SIZE. */ if (ret == 0) fsnotify_modify(file);
file_end_write(file); return ret;}EXPORT_SYMBOL_GPL(vfs_fallocate);
int ksys_fallocate(int fd, int mode, loff_t offset, loff_t len){ CLASS(fd, f)(fd);
if (fd_empty(f)) return -EBADF;
return vfs_fallocate(fd_file(f), mode, offset, len);}
SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len){ return ksys_fallocate(fd, mode, offset, len);}
#if defined(CONFIG_COMPAT) && defined(__ARCH_WANT_COMPAT_FALLOCATE)COMPAT_SYSCALL_DEFINE6(fallocate, int, fd, int, mode, compat_arg_u64_dual(offset), compat_arg_u64_dual(len)){ return ksys_fallocate(fd, mode, compat_arg_u64_glue(offset), compat_arg_u64_glue(len));}#endif
/* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and * switching the fsuid/fsgid around to the real ones. * * Creating new credentials is expensive, so we try to skip doing it, * which we can if the result would match what we already got. */static bool access_need_override_creds(int flags){ const struct cred *cred;
if (flags & AT_EACCESS) return false;
cred = current_cred(); if (!uid_eq(cred->fsuid, cred->uid) || !gid_eq(cred->fsgid, cred->gid)) return true;
if (!issecure(SECURE_NO_SETUID_FIXUP)) { kuid_t root_uid = make_kuid(cred->user_ns, 0); if (!uid_eq(cred->uid, root_uid)) { if (!cap_isclear(cred->cap_effective)) return true; } else { if (!cap_isidentical(cred->cap_effective, cred->cap_permitted)) return true; } }
return false;}
static const struct cred *access_override_creds(void){ struct cred *override_cred;
override_cred = prepare_creds(); if (!override_cred) return NULL;
/* * XXX access_need_override_creds performs checks in hopes of skipping * this work. Make sure it stays in sync if making any changes in this * routine. */
override_cred->fsuid = override_cred->uid; override_cred->fsgid = override_cred->gid;
if (!issecure(SECURE_NO_SETUID_FIXUP)) { /* Clear the capabilities if we switch to a non-root user */ kuid_t root_uid = make_kuid(override_cred->user_ns, 0); if (!uid_eq(override_cred->uid, root_uid)) cap_clear(override_cred->cap_effective); else override_cred->cap_effective = override_cred->cap_permitted; }
/* * The new set of credentials can *only* be used in * task-synchronous circumstances, and does not need * RCU freeing, unless somebody then takes a separate * reference to it. * * NOTE! This is _only_ true because this credential * is used purely for override_creds() that installs * it as the subjective cred. Other threads will be * accessing ->real_cred, not the subjective cred. * * If somebody _does_ make a copy of this (using the * 'get_current_cred()' function), that will clear the * non_rcu field, because now that other user may be * expecting RCU freeing. But normal thread-synchronous * cred accesses will keep things non-racy to avoid RCU * freeing. */ override_cred->non_rcu = 1; return override_creds(override_cred);}
static int do_faccessat(int dfd, const char __user *filename, int mode, int flags){ struct path path; struct inode *inode; int res; unsigned int lookup_flags = LOOKUP_FOLLOW; const struct cred *old_cred = NULL;
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL;
if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) return -EINVAL;
if (flags & AT_SYMLINK_NOFOLLOW) lookup_flags &= ~LOOKUP_FOLLOW;
if (access_need_override_creds(flags)) { old_cred = access_override_creds(); if (!old_cred) return -ENOMEM; }
CLASS(filename_uflags, name)(filename, flags);retry: res = filename_lookup(dfd, name, lookup_flags, &path, NULL); if (res) goto out;
inode = d_backing_inode(path.dentry);
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) { /* * MAY_EXEC on regular files is denied if the fs is mounted * with the "noexec" flag. */ res = -EACCES; if (path_noexec(&path)) goto out_path_release; }
res = inode_permission(mnt_idmap(path.mnt), inode, mode | MAY_ACCESS); /* SuS v2 requires we report a read only fs too */ if (res || !(mode & S_IWOTH) || special_file(inode->i_mode)) goto out_path_release; /* * This is a rare case where using __mnt_is_readonly() * is OK without a mnt_want/drop_write() pair. Since * no actual write to the fs is performed here, we do * not need to telegraph to that to anyone. * * By doing this, we accept that this access is * inherently racy and know that the fs may change * state before we even see this result. */ if (__mnt_is_readonly(path.mnt)) res = -EROFS;
out_path_release: path_put(&path); if (retry_estale(res, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; }out: if (old_cred) put_cred(revert_creds(old_cred));
return res;}
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode){ return do_faccessat(dfd, filename, mode, 0);}
SYSCALL_DEFINE4(faccessat2, int, dfd, const char __user *, filename, int, mode, int, flags){ return do_faccessat(dfd, filename, mode, flags);}
SYSCALL_DEFINE2(access, const char __user *, filename, int, mode){ return do_faccessat(AT_FDCWD, filename, mode, 0);}
SYSCALL_DEFINE1(chdir, const char __user *, filename){ struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; CLASS(filename, name)(filename);retry: error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL); if (!error) { error = path_permission(&path, MAY_EXEC | MAY_CHDIR); if (!error) set_fs_pwd(current->fs, &path); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } } return error;}
SYSCALL_DEFINE1(fchdir, unsigned int, fd){ int error;
if ((int)fd == FD_FAILFS_ROOT) return failfs_current_chdir();
CLASS(fd_raw, f)(fd); if (fd_empty(f)) return -EBADF;
if (!d_can_lookup(fd_file(f)->f_path.dentry)) return -ENOTDIR;
error = file_permission(fd_file(f), MAY_EXEC | MAY_CHDIR); if (!error) set_fs_pwd(current->fs, &fd_file(f)->f_path); return error;}
SYSCALL_DEFINE1(chroot, const char __user *, filename){ struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; CLASS(filename, name)(filename);retry: error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL); if (error) return error;
error = path_permission(&path, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out;
error = -EPERM; if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) goto dput_and_out; error = security_path_chroot(&path); if (!error) set_fs_root(current->fs, &path);dput_and_out: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } return error;}
SYSCALL_DEFINE2(fchroot, int, fd, unsigned int, flags){ struct path path; int error;
if (flags) return -EINVAL;
if (fd == FD_FAILFS_ROOT) { if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) { if (!task_no_new_privs(current)) return -EPERM; /* A shared fs_struct lets a sibling exec setuid past the check above. */ if (current->fs->users != 1) return -EINVAL; /* Moving the root to failfs lifts the old root's ".." barrier. */ if (current_chrooted()) return -EPERM; } failfs_get_root(&path); } else { CLASS(fd_raw, f)(fd); if (fd_empty(f)) return -EBADF;
if (!d_can_lookup(fd_file(f)->f_path.dentry)) return -ENOTDIR;
error = file_permission(fd_file(f), MAY_EXEC | MAY_CHDIR); if (error) return error;
if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) return -EPERM;
path = fd_file(f)->f_path; path_get(&path); }
error = security_path_chroot(&path); if (!error) set_fs_root(current->fs, &path); path_put(&path); return error;}
int chmod_common(const struct path *path, umode_t mode){ struct inode *inode = path->dentry->d_inode; struct delegated_inode delegated_inode = { }; struct iattr newattrs; int error;
error = mnt_want_write(path->mnt); if (error) return error;retry_deleg: error = inode_lock_killable(inode); if (error) goto out_mnt_unlock; error = security_path_chmod(path, mode); if (error) goto out_unlock; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; error = notify_change(mnt_idmap(path->mnt), path->dentry, &newattrs, &delegated_inode);out_unlock: inode_unlock(inode); if (is_delegated(&delegated_inode)) { error = break_deleg_wait(&delegated_inode); if (!error) goto retry_deleg; }out_mnt_unlock: mnt_drop_write(path->mnt); return error;}
int vfs_fchmod(struct file *file, umode_t mode){ audit_file(file); return chmod_common(&file->f_path, mode);}
SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode){ CLASS(fd, f)(fd);
if (fd_empty(f)) return -EBADF;
return vfs_fchmod(fd_file(f), mode);}
static int do_fchmodat(int dfd, const char __user *filename, umode_t mode, unsigned int flags){ struct path path; int error; unsigned int lookup_flags;
if (unlikely(flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))) return -EINVAL;
lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; CLASS(filename_uflags, name)(filename, flags);retry: error = filename_lookup(dfd, name, lookup_flags, &path, NULL); if (!error) { error = chmod_common(&path, mode); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } } return error;}
SYSCALL_DEFINE4(fchmodat2, int, dfd, const char __user *, filename, umode_t, mode, unsigned int, flags){ return do_fchmodat(dfd, filename, mode, flags);}
SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, umode_t, mode){ return do_fchmodat(dfd, filename, mode, 0);}
SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode){ return do_fchmodat(AT_FDCWD, filename, mode, 0);}
/* * Check whether @kuid is valid and if so generate and set vfsuid_t in * ia_vfsuid. * * Return: true if @kuid is valid, false if not. */static inline bool setattr_vfsuid(struct iattr *attr, kuid_t kuid){ if (!uid_valid(kuid)) return false; attr->ia_valid |= ATTR_UID; attr->ia_vfsuid = VFSUIDT_INIT(kuid); return true;}
/* * Check whether @kgid is valid and if so generate and set vfsgid_t in * ia_vfsgid. * * Return: true if @kgid is valid, false if not. */static inline bool setattr_vfsgid(struct iattr *attr, kgid_t kgid){ if (!gid_valid(kgid)) return false; attr->ia_valid |= ATTR_GID; attr->ia_vfsgid = VFSGIDT_INIT(kgid); return true;}
int chown_common(const struct path *path, uid_t user, gid_t group){ const struct mnt_idmap *idmap; struct user_namespace *fs_userns; struct inode *inode = path->dentry->d_inode; struct delegated_inode delegated_inode = { }; int error; struct iattr newattrs; kuid_t uid; kgid_t gid;
uid = make_kuid(current_user_ns(), user); gid = make_kgid(current_user_ns(), group);
idmap = mnt_idmap(path->mnt); fs_userns = i_user_ns(inode);
retry_deleg: newattrs.ia_vfsuid = INVALID_VFSUID; newattrs.ia_vfsgid = INVALID_VFSGID; newattrs.ia_valid = ATTR_CTIME; if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid)) return -EINVAL; if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid)) return -EINVAL; error = inode_lock_killable(inode); if (error) return error; if (!S_ISDIR(inode->i_mode)) newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV | setattr_should_drop_sgid(idmap, inode); /* Continue to send actual fs values, not the mount values. */ error = security_path_chown( path, from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid), from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid)); if (!error) error = notify_change(idmap, path->dentry, &newattrs, &delegated_inode); inode_unlock(inode); if (is_delegated(&delegated_inode)) { error = break_deleg_wait(&delegated_inode); if (!error) goto retry_deleg; } return error;}
int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag){ struct path path; int error; int lookup_flags;
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) return -EINVAL;
lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; CLASS(filename_uflags, name)(filename, flag);retry: error = filename_lookup(dfd, name, lookup_flags, &path, NULL); if (!error) { error = mnt_want_write(path.mnt); if (!error) { error = chown_common(&path, user, group); mnt_drop_write(path.mnt); } path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } } return error;}
SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user, gid_t, group, int, flag){ return do_fchownat(dfd, filename, user, group, flag);}
SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group){ return do_fchownat(AT_FDCWD, filename, user, group, 0);}
SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group){ return do_fchownat(AT_FDCWD, filename, user, group, AT_SYMLINK_NOFOLLOW);}
int vfs_fchown(struct file *file, uid_t user, gid_t group){ int error;
error = mnt_want_write_file(file); if (error) return error; audit_file(file); error = chown_common(&file->f_path, user, group); mnt_drop_write_file(file); return error;}
int ksys_fchown(unsigned int fd, uid_t user, gid_t group){ CLASS(fd, f)(fd);
if (fd_empty(f)) return -EBADF;
return vfs_fchown(fd_file(f), user, group);}
SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group){ return ksys_fchown(fd, user, group);}
static inline int file_get_write_access(struct file *f){ int error;
error = get_write_access(f->f_inode); if (unlikely(error)) return error; error = mnt_get_write_access(f->f_path.mnt); if (unlikely(error)) goto cleanup_inode; if (unlikely(f->f_mode & FMODE_BACKING)) { error = mnt_get_write_access(backing_file_user_path(f)->mnt); if (unlikely(error)) goto cleanup_mnt; } return 0;
cleanup_mnt: mnt_put_write_access(f->f_path.mnt);cleanup_inode: put_write_access(f->f_inode); return error;}
/* * Populate struct file * * NOTE: it assumes f_path is populated and consumes the caller's reference. */static int do_dentry_open(struct file *f, int (*open)(struct inode *, struct file *)){ static const struct file_operations empty_fops = {}; struct inode *inode = f->f_path.dentry->d_inode; int error;
f->f_inode = inode; f->f_mapping = inode->i_mapping; f->f_wb_err = filemap_sample_wb_err(f->f_mapping); f->f_sb_err = file_sample_sb_err(f);
if (unlikely(f->f_flags & O_PATH)) { f->f_mode = FMODE_PATH | FMODE_OPENED; file_set_fsnotify_mode(f, FMODE_NONOTIFY); f->f_op = &empty_fops; return 0; }
if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { i_readcount_inc(inode); } else if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) { error = file_get_write_access(f); if (unlikely(error)) goto cleanup_file; f->f_mode |= FMODE_WRITER; }
/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */ if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) f->f_mode |= FMODE_ATOMIC_POS;
f->f_op = fops_get(inode->i_fop); if (WARN_ON(!f->f_op)) { error = -ENODEV; goto cleanup_all; }
error = security_file_open(f); if (unlikely(error)) goto cleanup_all;
/* * Call fsnotify open permission hook and set FMODE_NONOTIFY_* bits * according to existing permission watches. * If FMODE_NONOTIFY mode was already set for an fanotify fd or for a * pseudo file, this call will not change the mode. */ error = fsnotify_open_perm_and_set_mode(f); if (unlikely(error)) goto cleanup_all;
error = break_lease(file_inode(f), f->f_flags); if (unlikely(error)) goto cleanup_all;
/* normally all 3 are set; ->open() can clear them if needed */ f->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; if (!open) open = f->f_op->open; if (open) { error = open(inode, f); if (error) goto cleanup_all; } f->f_mode |= FMODE_OPENED; if ((f->f_mode & FMODE_READ) && likely(f->f_op->read || f->f_op->read_iter)) f->f_mode |= FMODE_CAN_READ; if ((f->f_mode & FMODE_WRITE) && likely(f->f_op->write || f->f_op->write_iter)) f->f_mode |= FMODE_CAN_WRITE; if ((f->f_mode & FMODE_LSEEK) && !f->f_op->llseek) f->f_mode &= ~FMODE_LSEEK; if (f->f_mapping->a_ops && f->f_mapping->a_ops->direct_IO) f->f_mode |= FMODE_CAN_ODIRECT;
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | __O_REGULAR); f->f_iocb_flags = iocb_flags(f);
file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
if ((f->f_flags & O_DIRECT) && !(f->f_mode & FMODE_CAN_ODIRECT)) return -EINVAL;
return 0;
cleanup_all: if (WARN_ON_ONCE(error > 0)) error = -EINVAL; fops_put(f->f_op); put_file_access(f);cleanup_file: path_put(&f->f_path); f->__f_path.mnt = NULL; f->__f_path.dentry = NULL; f->f_inode = NULL; return error;}
/** * finish_open - finish opening a file * @file: file pointer * @dentry: pointer to dentry * @open: open callback * * This can be used to finish opening a file passed to i_op->atomic_open(). * * If the open callback is set to NULL, then the standard f_op->open() * filesystem callback is substituted. * * NB: the dentry reference is _not_ consumed. If, for example, the dentry is * the return value of d_splice_alias(), then the caller needs to perform dput() * on it after finish_open(). * * Returns zero on success or -errno if the open failed. */int finish_open(struct file *file, struct dentry *dentry, int (*open)(struct inode *, struct file *)){ BUG_ON(file->f_mode & FMODE_OPENED); /* once it's opened, it's opened */
file->__f_path.dentry = dentry; path_get(&file->f_path); return do_dentry_open(file, open);}EXPORT_SYMBOL(finish_open);
/** * finish_no_open - finish ->atomic_open() without opening the file * * @file: file pointer * @dentry: dentry, ERR_PTR(-E...) or NULL (as returned from ->lookup()) * * This can be used to set the result of a lookup in ->atomic_open(). * * NB: unlike finish_open() this function does consume the dentry reference and * the caller need not dput() it. * * Returns 0 or -E..., which must be the return value of ->atomic_open() after * having called this function. */int finish_no_open(struct file *file, struct dentry *dentry){ if (IS_ERR(dentry)) return PTR_ERR(dentry); file->__f_path.dentry = dentry; return 0;}EXPORT_SYMBOL(finish_no_open);
char *file_path(struct file *filp, char *buf, int buflen){ return d_path(&filp->f_path, buf, buflen);}EXPORT_SYMBOL(file_path);
/** * vfs_open - open the file at the given path * @path: path to open * @file: newly allocated file with f_flag initialized */int vfs_open(const struct path *path, struct file *file){ int ret;
file->__f_path = *path; path_get(&file->f_path); ret = do_dentry_open(file, NULL); if (!ret) { /* * Once we return a file with FMODE_OPENED, __fput() will call * fsnotify_close(), so we need fsnotify_open() here for * symmetry. */ fsnotify_open(file); } return ret;}
/** * vfs_open_consume - open the file at the given path and consume the reference * @path: path to open * @file: newly allocated file with f_flag initialized */int vfs_open_consume(struct path *path, struct file *file){ int ret;
file->__f_path = *path; path->mnt = NULL; path->dentry = NULL; ret = do_dentry_open(file, NULL); if (!ret) { fsnotify_open(file); } return ret;}
struct file *dentry_open(const struct path *path, int flags, const struct cred *cred){ int error; struct file *f;
/* We must always pass in a valid mount pointer. */ BUG_ON(!path->mnt);
f = alloc_empty_file(flags, cred); if (!IS_ERR(f)) { error = vfs_open(path, f); if (error) { fput(f); f = ERR_PTR(error); } } return f;}EXPORT_SYMBOL(dentry_open);
struct file *dentry_open_nonotify(const struct path *path, int flags, const struct cred *cred){ struct file *f = alloc_empty_file(flags, cred); if (!IS_ERR(f)) { int error;
file_set_fsnotify_mode(f, FMODE_NONOTIFY); error = vfs_open(path, f); if (error) { fput(f); f = ERR_PTR(error); } } return f;}
/** * kernel_file_open - open a file for kernel internal use * @path: path of the file to open * @flags: open flags * @cred: credentials for open * * Open a file for use by in-kernel consumers. The file is not accounted * against nr_files and must not be installed into the file descriptor * table. * * Return: Opened file on success, an error pointer on failure. */struct file *kernel_file_open(const struct path *path, int flags, const struct cred *cred){ struct file *f; int error;
f = alloc_empty_file_noaccount(flags, cred); if (IS_ERR(f)) return f;
error = vfs_open(path, f); if (error) { fput(f); return ERR_PTR(error); } return f;}EXPORT_SYMBOL_GPL(kernel_file_open);
#define WILL_CREATE(flags) (flags & (O_CREAT | __O_TMPFILE))#define O_PATH_FLAGS (O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC | O_EMPTYPATH)
inline struct open_how build_open_how(int flags, umode_t mode){ struct open_how how = { .flags = flags & VALID_OPEN_FLAGS, .mode = mode & S_IALLUGO, };
/* O_PATH beats everything else. */ if (how.flags & O_PATH) how.flags &= O_PATH_FLAGS; /* Modes should only be set for create-like flags. */ if (!WILL_CREATE(how.flags)) how.mode = 0; return how;}
inline int build_open_flags(const struct open_how *how, struct open_flags *op){ u64 flags = how->flags; u64 strip = O_CLOEXEC; int lookup_flags = 0; int acc_mode = ACC_MODE(flags);
BUILD_BUG_ON_MSG(upper_32_bits(VALID_OPEN_FLAGS), "VALID_OPEN_FLAGS must fit in 32 bits"); /* The whole point: OPENAT2_REGULAR must be unrepresentable in int. */ BUILD_BUG_ON_MSG(!upper_32_bits(OPENAT2_REGULAR), "OPENAT2_REGULAR must live in the upper 32 bits of open_how::flags"); /* Prevent a future bit collision between UAPI and internal carrier. */ BUILD_BUG_ON_MSG(OPENAT2_REGULAR & VALID_OPEN_FLAGS, "OPENAT2_REGULAR must not alias any open()/openat() flag"); BUILD_BUG_ON_MSG(__O_REGULAR & VALID_OPENAT2_FLAGS, "__O_REGULAR must not alias any user-visible flag");
/* * Strip flags that aren't relevant in determining struct open_flags. */ flags &= ~strip;
/* * Older syscalls implicitly clear all of the invalid flags or argument * values before calling build_open_flags(), but openat2(2) checks all * of its arguments. */ if (flags & ~VALID_OPENAT2_FLAGS) return -EINVAL; if (how->resolve & ~VALID_RESOLVE_FLAGS) return -EINVAL;
/* Scoping flags are mutually exclusive. */ if ((how->resolve & RESOLVE_BENEATH) && (how->resolve & RESOLVE_IN_ROOT)) return -EINVAL;
/* Deal with the mode. */ if (WILL_CREATE(flags)) { if (how->mode & ~S_IALLUGO) return -EINVAL; op->mode = how->mode | S_IFREG; } else { if (how->mode != 0) return -EINVAL; op->mode = 0; }
/* * Block bugs where O_DIRECTORY | O_CREAT created regular files. * Note, that blocking O_DIRECTORY | O_CREAT here also protects * O_TMPFILE below which requires O_DIRECTORY being raised. */ if ((flags & (O_DIRECTORY | O_CREAT)) == (O_DIRECTORY | O_CREAT)) return -EINVAL;
/* Now handle the creative implementation of O_TMPFILE. */ if (flags & __O_TMPFILE) { /* * In order to ensure programs get explicit errors when trying * to use O_TMPFILE on old kernels we enforce that O_DIRECTORY * is raised alongside __O_TMPFILE. */ if (!(flags & O_DIRECTORY)) return -EINVAL; if (!(acc_mode & MAY_WRITE)) return -EINVAL; } /* * Asking to open a directory and a regular file at the same time is * contradictory. */ if ((flags & (O_DIRECTORY | OPENAT2_REGULAR)) == (O_DIRECTORY | OPENAT2_REGULAR)) return -EINVAL;
if (flags & O_PATH) { /* O_PATH only permits certain other flags to be set. */ if (flags & ~O_PATH_FLAGS) return -EINVAL; acc_mode = 0; }
/* * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only * check for O_DSYNC if the need any syncing at all we enforce it's * always set instead of having to deal with possibly weird behaviour * for malicious applications setting only __O_SYNC. */ if (flags & __O_SYNC) flags |= O_DSYNC;
/* * Translate the upper-32-bit UAPI bit OPENAT2_REGULAR into the * kernel-internal lower-32-bit __O_REGULAR carrier so the bit * survives the assignment to op->open_flag (an int) below and the * subsequent flow through f->f_flags (unsigned int) and the * i_op->atomic_open() callback (unsigned). do_dentry_open() strips * __O_REGULAR before the file becomes visible to userspace. */ if (flags & OPENAT2_REGULAR) { flags &= ~OPENAT2_REGULAR; flags |= __O_REGULAR; }
op->open_flag = flags;
/* O_TRUNC implies we need access checks for write permissions */ if (flags & O_TRUNC) acc_mode |= MAY_WRITE;
/* Allow the LSM permission hook to distinguish append access from general write access. */ if (flags & O_APPEND) acc_mode |= MAY_APPEND;
op->acc_mode = acc_mode;
op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;
if (flags & O_CREAT) { op->intent |= LOOKUP_CREATE; if (flags & O_EXCL) { op->intent |= LOOKUP_EXCL; flags |= O_NOFOLLOW; } }
if (flags & O_DIRECTORY) lookup_flags |= LOOKUP_DIRECTORY; if (!(flags & O_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; if (flags & O_EMPTYPATH) lookup_flags |= LOOKUP_EMPTY;
if (how->resolve & RESOLVE_NO_XDEV) lookup_flags |= LOOKUP_NO_XDEV; if (how->resolve & RESOLVE_NO_MAGICLINKS) lookup_flags |= LOOKUP_NO_MAGICLINKS; if (how->resolve & RESOLVE_NO_SYMLINKS) lookup_flags |= LOOKUP_NO_SYMLINKS; if (how->resolve & RESOLVE_BENEATH) lookup_flags |= LOOKUP_BENEATH; if (how->resolve & RESOLVE_IN_ROOT) lookup_flags |= LOOKUP_IN_ROOT; if (how->resolve & RESOLVE_CACHED) { /* Don't bother even trying for create/truncate/tmpfile open */ if (flags & (O_TRUNC | O_CREAT | __O_TMPFILE)) return -EAGAIN; lookup_flags |= LOOKUP_CACHED; }
op->lookup_flags = lookup_flags; return 0;}
/** * file_open_name - open file and return file pointer * * @name: struct filename containing path to open * @flags: open flags as per the open(2) second argument * @mode: mode for the new file if O_CREAT is set, else ignored * * This is the helper to open a file from kernelspace if you really * have to. But in generally you should not do this, so please move * along, nothing to see here.. */struct file *file_open_name(struct filename *name, int flags, umode_t mode){ struct open_flags op; struct open_how how = build_open_how(flags, mode); int err = build_open_flags(&how, &op); if (err) return ERR_PTR(err); return do_file_open(AT_FDCWD, name, &op);}
/** * filp_open - open file and return file pointer * * @filename: path to open * @flags: open flags as per the open(2) second argument * @mode: mode for the new file if O_CREAT is set, else ignored * * This is the helper to open a file from kernelspace if you really * have to. But in generally you should not do this, so please move * along, nothing to see here.. */struct file *filp_open(const char *filename, int flags, umode_t mode){ CLASS(filename_kernel, name)(filename); return file_open_name(name, flags, mode);}EXPORT_SYMBOL(filp_open);
struct file *file_open_root(const struct path *root, const char *filename, int flags, umode_t mode){ struct open_flags op; struct open_how how = build_open_how(flags, mode); int err = build_open_flags(&how, &op); if (err) return ERR_PTR(err); return do_file_open_root(root, filename, &op);}EXPORT_SYMBOL(file_open_root);
static int do_sys_openat2(int dfd, const char __user *filename, struct open_how *how){ struct open_flags op; int err = build_open_flags(how, &op); if (unlikely(err)) return err;
CLASS(filename_flags, name)(filename, op.lookup_flags); return FD_ADD(how->flags, do_file_open(dfd, name, &op));}
int do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode){ struct open_how how = build_open_how(flags, mode); return do_sys_openat2(dfd, filename, &how);}
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode){ if (force_o_largefile()) flags |= O_LARGEFILE; return do_sys_open(AT_FDCWD, filename, flags, mode);}
SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, umode_t, mode){ if (force_o_largefile()) flags |= O_LARGEFILE; return do_sys_open(dfd, filename, flags, mode);}
SYSCALL_DEFINE4(openat2, int, dfd, const char __user *, filename, struct open_how __user *, how, size_t, usize){ int err; struct open_how tmp;
BUILD_BUG_ON(sizeof(struct open_how) < OPEN_HOW_SIZE_VER0); BUILD_BUG_ON(sizeof(struct open_how) != OPEN_HOW_SIZE_LATEST);
if (unlikely(usize < OPEN_HOW_SIZE_VER0)) return -EINVAL; if (unlikely(usize > PAGE_SIZE)) return -E2BIG;
err = copy_struct_from_user(&tmp, sizeof(tmp), how, usize); if (err) return err;
audit_openat2_how(&tmp);
/* O_LARGEFILE is only allowed for non-O_PATH. */ if (!(tmp.flags & O_PATH) && force_o_largefile()) tmp.flags |= O_LARGEFILE;
return do_sys_openat2(dfd, filename, &tmp);}
#ifdef CONFIG_COMPAT/* * Exactly like sys_open(), except that it doesn't set the * O_LARGEFILE flag. */COMPAT_SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode){ return do_sys_open(AT_FDCWD, filename, flags, mode);}
/* * Exactly like sys_openat(), except that it doesn't set the * O_LARGEFILE flag. */COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, umode_t, mode){ return do_sys_open(dfd, filename, flags, mode);}#endif
#ifndef __alpha__
/* * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode){ int flags = O_CREAT | O_WRONLY | O_TRUNC;
if (force_o_largefile()) flags |= O_LARGEFILE; return do_sys_open(AT_FDCWD, pathname, flags, mode);}#endif
/* * "id" is the POSIX thread ID. We use the * files pointer for this.. */static int filp_flush(struct file *filp, fl_owner_t id){ int retval = 0;
if (CHECK_DATA_CORRUPTION(file_count(filp) == 0, filp, "VFS: Close: file count is 0 (f_op=%ps)", filp->f_op)) { return 0; }
if (filp->f_op->flush) retval = filp->f_op->flush(filp, id);
if (likely(!(filp->f_mode & FMODE_PATH))) { dnotify_flush(filp, id); locks_remove_posix(filp, id); } return retval;}
int filp_close(struct file *filp, fl_owner_t id){ int retval;
retval = filp_flush(filp, id); fput_close(filp);
return retval;}EXPORT_SYMBOL(filp_close);
/* Like filp_close() but the last reference is put right here. */int filp_close_sync(struct file *filp, fl_owner_t id){ int retval;
/* Kernel threads must never put their final reference here. */ VFS_WARN_ON_ONCE(current->flags & PF_KTHREAD); retval = filp_flush(filp, id); fput_close_sync(filp);
return retval;}
/* * Careful here! We test whether the file pointer is NULL before * releasing the fd. This ensures that one clone task can't release * an fd while another clone is opening it. */SYSCALL_DEFINE1(close, unsigned int, fd){ int retval; struct file *file;
file = file_close_fd(fd); if (!file) return -EBADF;
/* * We're returning to user space. Don't bother * with any delayed fput() cases. */ retval = filp_close_sync(file, current->files);
if (likely(retval == 0)) return 0;
/* can't restart close syscall because file table entry was cleared */ if (retval == -ERESTARTSYS || retval == -ERESTARTNOINTR || retval == -ERESTARTNOHAND || retval == -ERESTART_RESTARTBLOCK) retval = -EINTR;
return retval;}
/* * This routine simulates a hangup on the tty, to arrange that users * are given clean terminals at login time. */SYSCALL_DEFINE0(vhangup){ if (capable(CAP_SYS_TTY_CONFIG)) { tty_vhangup_self(); return 0; } return -EPERM;}
/* * Called when an inode is about to be open. * We use this to disallow opening large files on 32bit systems if * the caller didn't specify O_LARGEFILE. On 64bit systems we force * on this flag in sys_open. */int generic_file_open(struct inode * inode, struct file * filp){ if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS) return -EOVERFLOW; return 0;}
EXPORT_SYMBOL(generic_file_open);
/* * This is used by subsystems that don't want seekable * file descriptors. The function is not supposed to ever fail, the only * reason it returns an 'int' and not 'void' is so that it can be plugged * directly into file_operations structure. */int nonseekable_open(struct inode *inode, struct file *filp){ filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); return 0;}
EXPORT_SYMBOL(nonseekable_open);
/* * stream_open is used by subsystems that want stream-like file descriptors. * Such file descriptors are not seekable and don't have notion of position * (file.f_pos is always 0 and ppos passed to .read()/.write() is always NULL). * Contrary to file descriptors of other regular files, .read() and .write() * can run simultaneously. * * stream_open never fails and is marked to return int so that it could be * directly used as file_operations.open . */int stream_open(struct inode *inode, struct file *filp){ filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE | FMODE_ATOMIC_POS); filp->f_mode |= FMODE_STREAM; return 0;}
EXPORT_SYMBOL(stream_open);