Skip to content
1 change: 1 addition & 0 deletions arch/alpha/include/uapi/asm/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define O_SYNC (__O_SYNC|O_DSYNC)

#define O_PATH 040000000
#define __O_TMPFILE 0100000000

#define F_GETLK 7
#define F_SETLK 8
Expand Down
2 changes: 1 addition & 1 deletion arch/arm64/configs/lineage_s2_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
CONFIG_RFKILL_PM=y
CONFIG_RFKILL_LEDS=y
# CONFIG_RFKILL_INPUT is not set
CONFIG_RFKILL_INPUT=y
# CONFIG_RFKILL_REGULATOR is not set
# CONFIG_RFKILL_GPIO is not set
# CONFIG_NET_9P is not set
Expand Down
4 changes: 4 additions & 0 deletions arch/arm64/include/uapi/asm/bitsperlong.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
#ifndef __ASM_BITSPERLONG_H
#define __ASM_BITSPERLONG_H

#ifdef __aarch64__
#define __BITS_PER_LONG 64
#else
#define __BITS_PER_LONG 32
#endif

#include <asm-generic/bitsperlong.h>

Expand Down
1 change: 1 addition & 0 deletions arch/parisc/include/uapi/asm/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#define O_INVISIBLE 004000000 /* invisible I/O, for DMAPI/XDSM */

#define O_PATH 020000000
#define __O_TMPFILE 040000000

#define F_GETLK64 8
#define F_SETLK64 9
Expand Down
1 change: 1 addition & 0 deletions arch/sparc/include/uapi/asm/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#define O_SYNC (__O_SYNC|O_DSYNC)

#define O_PATH 0x1000000
#define __O_TMPFILE 0x2000000

#define F_GETOWN 5 /* for sockets. */
#define F_SETOWN 6 /* for sockets. */
Expand Down
16 changes: 16 additions & 0 deletions fs/dcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -3094,6 +3094,22 @@ void d_genocide(struct dentry *root)
goto again;
}

void d_tmpfile(struct dentry *dentry, struct inode *inode)
{
inode_dec_link_count(inode);
BUG_ON(dentry->d_name.name != dentry->d_iname ||
!hlist_unhashed(&dentry->d_u.d_alias) ||
!d_unlinked(dentry));
spin_lock(&dentry->d_parent->d_lock);
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
dentry->d_name.len = sprintf(dentry->d_iname, "#%llu",
(unsigned long long)inode->i_ino);
spin_unlock(&dentry->d_lock);
spin_unlock(&dentry->d_parent->d_lock);
d_instantiate(dentry, inode);
}
EXPORT_SYMBOL(d_tmpfile);

/**
* find_inode_number - check for dentry with name
* @dir: directory to check
Expand Down
24 changes: 24 additions & 0 deletions fs/ext2/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,29 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, umode_t mode
return ext2_add_nondir(dentry, inode);
}

static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
{
struct inode *inode = ext2_new_inode(dir, mode, NULL);
if (IS_ERR(inode))
return PTR_ERR(inode);

inode->i_op = &ext2_file_inode_operations;
if (ext2_use_xip(inode->i_sb)) {
inode->i_mapping->a_ops = &ext2_aops_xip;
inode->i_fop = &ext2_xip_file_operations;
} else if (test_opt(inode->i_sb, NOBH)) {
inode->i_mapping->a_ops = &ext2_nobh_aops;
inode->i_fop = &ext2_file_operations;
} else {
inode->i_mapping->a_ops = &ext2_aops;
inode->i_fop = &ext2_file_operations;
}
mark_inode_dirty(inode);
d_tmpfile(dentry, inode);
unlock_new_inode(inode);
return 0;
}

static int ext2_mknod (struct inode * dir, struct dentry *dentry, umode_t mode, dev_t rdev)
{
struct inode * inode;
Expand Down Expand Up @@ -398,6 +421,7 @@ const struct inode_operations ext2_dir_inode_operations = {
#endif
.setattr = ext2_setattr,
.get_acl = ext2_get_acl,
.tmpfile = ext2_tmpfile,
};

const struct inode_operations ext2_special_inode_operations = {
Expand Down
4 changes: 3 additions & 1 deletion fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,10 @@ EXPORT_SYMBOL(set_nlink);
*/
void inc_nlink(struct inode *inode)
{
if (WARN_ON(inode->i_nlink == 0))
if (unlikely(inode->i_nlink == 0)) {
WARN_ON(!(inode->i_state & I_LINKABLE));
atomic_long_dec(&inode->i_sb->s_remove_count);
}

inode->__i_nlink++;
}
Expand Down
13 changes: 13 additions & 0 deletions fs/minix/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ static int minix_mknod(struct inode * dir, struct dentry *dentry, umode_t mode,
return error;
}

static int minix_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int error;
struct inode *inode = minix_new_inode(dir, mode, &error);
if (inode) {
minix_set_inode(inode, 0);
mark_inode_dirty(inode);
d_tmpfile(dentry, inode);
}
return error;
}

static int minix_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl)
{
Expand Down Expand Up @@ -254,4 +266,5 @@ const struct inode_operations minix_dir_inode_operations = {
.mknod = minix_mknod,
.rename = minix_rename,
.getattr = minix_getattr,
.tmpfile = minix_tmpfile,
};
75 changes: 74 additions & 1 deletion fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -3003,6 +3003,68 @@ static int do_last(struct nameidata *nd, struct path *path,
goto retry_lookup;
}

static int do_tmpfile(int dfd, struct filename *pathname,
struct nameidata *nd, int flags,
const struct open_flags *op,
struct file *file, int *opened)
{
static const struct qstr name = QSTR_INIT("/", 1);
struct dentry *dentry, *child;
struct inode *dir;
int error = path_lookupat(dfd, pathname->name,
flags | LOOKUP_DIRECTORY, nd);
if (unlikely(error))
return error;
error = mnt_want_write(nd->path.mnt);
if (unlikely(error))
goto out;
/* we want directory to be writable */
error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC);
if (error)
goto out2;
dentry = nd->path.dentry;
dir = dentry->d_inode;
if (!dir->i_op->tmpfile) {
error = -EOPNOTSUPP;
goto out2;
}
child = d_alloc(dentry, &name);
if (unlikely(!child)) {
error = -ENOMEM;
goto out2;
}
nd->flags &= ~LOOKUP_DIRECTORY;
nd->flags |= op->intent;
dput(nd->path.dentry);
nd->path.dentry = child;
error = dir->i_op->tmpfile(dir, nd->path.dentry, op->mode);
if (error)
goto out2;
audit_inode(pathname, nd->path.dentry, 0);
/* Don't check for other permissions, the inode was just created */
error = may_open(&nd->path, MAY_OPEN, op->open_flag);
if (error)
goto out2;
file->f_path.mnt = nd->path.mnt;
error = finish_open(file, nd->path.dentry, NULL, opened);
if (error)
goto out2;
error = open_check_o_direct(file);
if (error) {
fput(file);
} else if (!(op->open_flag & O_EXCL)) {
struct inode *inode = file_inode(file);
spin_lock(&inode->i_lock);
inode->i_state |= I_LINKABLE;
spin_unlock(&inode->i_lock);
}
out2:
mnt_drop_write(nd->path.mnt);
out:
path_put(&nd->path);
return error;
}

static struct file *path_openat(int dfd, struct filename *pathname,
struct nameidata *nd, const struct open_flags *op, int flags)
{
Expand All @@ -3018,6 +3080,11 @@ static struct file *path_openat(int dfd, struct filename *pathname,

file->f_flags = op->open_flag;

if (unlikely(file->f_flags & __O_TMPFILE)) {
error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened);
goto out;
}

error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);
if (unlikely(error))
goto out;
Expand Down Expand Up @@ -3711,12 +3778,18 @@ int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir

mutex_lock(&inode->i_mutex);
/* Make sure we don't allow creating hardlink to an unlinked file */
if (inode->i_nlink == 0)
if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))
error = -ENOENT;
else if (max_links && inode->i_nlink >= max_links)
error = -EMLINK;
else
error = dir->i_op->link(old_dentry, dir, new_dentry);

if (!error && (inode->i_state & I_LINKABLE)) {
spin_lock(&inode->i_lock);
inode->i_state &= ~I_LINKABLE;
spin_unlock(&inode->i_lock);
}
mutex_unlock(&inode->i_mutex);
if (!error)
fsnotify_link(dir, inode, new_dentry);
Expand Down
14 changes: 9 additions & 5 deletions fs/open.c
Original file line number Diff line number Diff line change
Expand Up @@ -839,11 +839,15 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
if (flags & __O_SYNC)
flags |= O_DSYNC;

/*
* If we have O_PATH in the open flag. Then we
* cannot have anything other than the below set of flags
*/
if (flags & O_PATH) {
if (flags & __O_TMPFILE) {
if ((flags & O_TMPFILE_MASK) != O_TMPFILE)
return -EINVAL;
acc_mode = MAY_OPEN | ACC_MODE(flags);
} else if (flags & O_PATH) {
/*
* If we have O_PATH in the open flag. Then we
* cannot have anything other than the below set of flags
*/
flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
acc_mode = 0;
} else {
Expand Down
2 changes: 2 additions & 0 deletions include/linux/dcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ extern struct dentry * d_make_root(struct inode *);
/* <clickety>-<click> the ramfs-type tree */
extern void d_genocide(struct dentry *);

extern void d_tmpfile(struct dentry *, struct inode *);

extern struct dentry *d_find_alias(struct inode *);
extern void d_prune_aliases(struct inode *);

Expand Down
2 changes: 2 additions & 0 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,7 @@ struct inode_operations {
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode, int *opened);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
} ____cacheline_aligned;

ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
Expand Down Expand Up @@ -1759,6 +1760,7 @@ struct super_operations {
#define I_REFERENCED (1 << 8)
#define __I_DIO_WAKEUP 9
#define I_DIO_WAKEUP (1 << I_DIO_WAKEUP)
#define I_LINKABLE (1 << 10)

#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)

Expand Down
8 changes: 8 additions & 0 deletions include/uapi/asm-generic/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@
#define O_PATH 010000000
#endif

#ifndef __O_TMPFILE
#define __O_TMPFILE 020000000
#endif

/* a horrid kludge trying to make sure that this will fail on old kernels */
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY | O_RDWR)
#define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT | O_ACCMODE)

#ifndef O_NDELAY
#define O_NDELAY O_NONBLOCK
#endif
Expand Down
15 changes: 13 additions & 2 deletions include/uapi/linux/rmnet_data.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
* Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
Expand Down Expand Up @@ -223,8 +223,19 @@ enum rmnet_netlink_message_types_e {
* uint32_t MAP Flow Handle
* Returns: status code
*/
RMNET_NETLINK_DEL_VND_TC_FLOW
RMNET_NETLINK_DEL_VND_TC_FLOW,

/*
* RMNET_NETLINK_NEW_VND_WITH_NAME - Creates a new virtual network
* device node with the specified
* device name
* Args: int32_t node number
* char[] vnd_name - Use as name
* Returns: status code
*/
RMNET_NETLINK_NEW_VND_WITH_NAME
};
#define RMNET_NETLINK_NEW_VND_WITH_NAME RMNET_NETLINK_NEW_VND_WITH_NAME

enum rmnet_config_endpoint_modes_e {
/* Pass the frame up the stack with no modifications to skb->dev */
Expand Down
32 changes: 32 additions & 0 deletions mm/shmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -2047,6 +2047,37 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
return error;
}

static int
shmem_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
{
struct inode *inode;
int error = -ENOSPC;

inode = shmem_get_inode(dir->i_sb, dir, mode, 0, VM_NORESERVE);
if (inode) {
error = security_inode_init_security(inode, dir,
NULL,
shmem_initxattrs, NULL);
if (error) {
if (error != -EOPNOTSUPP) {
iput(inode);
return error;
}
}
#ifdef CONFIG_TMPFS_POSIX_ACL
error = generic_acl_init(inode, dir);
if (error) {
iput(inode);
return error;
}
#else
error = 0;
#endif
d_tmpfile(dentry, inode);
}
return error;
}

static int shmem_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int error;
Expand Down Expand Up @@ -2807,6 +2838,7 @@ static const struct inode_operations shmem_dir_inode_operations = {
.rmdir = shmem_rmdir,
.mknod = shmem_mknod,
.rename = shmem_rename,
.tmpfile = shmem_tmpfile,
#endif
#ifdef CONFIG_TMPFS_XATTR
.setxattr = shmem_setxattr,
Expand Down
Loading