int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, u64 child_nodeid, struct qstr *name) { int err = -ENOTDIR; struct inode *parent; struct dentry *dir; struct dentry *entry; parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid); if (!parent) return -ENOENT; mutex_lock(&parent->i_mutex); if (!S_ISDIR(parent->i_mode)) goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) goto unlock; entry = d_lookup(dir, name); dput(dir); if (!entry) goto unlock; fuse_invalidate_attr(parent); fuse_invalidate_entry(entry); if (child_nodeid != 0 && entry->d_inode) { mutex_lock(&entry->d_inode->i_mutex); if (get_node_id(entry->d_inode) != child_nodeid) { err = -ENOENT; goto badentry; } if (d_mountpoint(entry)) { err = -EBUSY; goto badentry; } if (S_ISDIR(entry->d_inode->i_mode)) { shrink_dcache_parent(entry); if (!simple_empty(entry)) { err = -ENOTEMPTY; goto badentry; } entry->d_inode->i_flags |= S_DEAD; } dont_mount(entry); clear_nlink(entry->d_inode); err = 0; badentry: mutex_unlock(&entry->d_inode->i_mutex); if (!err) d_delete(entry); } else { err = 0; } dput(entry); unlock: mutex_unlock(&parent->i_mutex); iput(parent); return err; }
/** * dump_common_audit_data - helper to dump common audit data * @a : common audit data * */ static void dump_common_audit_data(struct audit_buffer *ab, struct common_audit_data *a) { char comm[sizeof(current->comm)]; /* * To keep stack sizes in check force programers to notice if they * start making this union too large! See struct lsm_network_audit * as an example of how to deal with large data. */ BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); audit_log_format(ab, " pid=%d comm=", task_pid_nr(current)); audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm))); switch (a->type) { case LSM_AUDIT_DATA_NONE: return; case LSM_AUDIT_DATA_IPC: audit_log_format(ab, " key=%d ", a->u.ipc_id); break; case LSM_AUDIT_DATA_CAP: audit_log_format(ab, " capability=%d ", a->u.cap); break; case LSM_AUDIT_DATA_PATH: { struct inode *inode; audit_log_d_path(ab, " path=", &a->u.path); inode = d_backing_inode(a->u.path.dentry); if (inode) { audit_log_format(ab, " dev="); audit_log_untrustedstring(ab, inode->i_sb->s_id); audit_log_format(ab, " ino=%lu", inode->i_ino); } break; } case LSM_AUDIT_DATA_DENTRY: { struct inode *inode; audit_log_format(ab, " name="); audit_log_untrustedstring(ab, a->u.dentry->d_name.name); inode = d_backing_inode(a->u.dentry); if (inode) { audit_log_format(ab, " dev="); audit_log_untrustedstring(ab, inode->i_sb->s_id); audit_log_format(ab, " ino=%lu", inode->i_ino); } break; } case LSM_AUDIT_DATA_INODE: { struct dentry *dentry; struct inode *inode; inode = a->u.inode; dentry = d_find_alias(inode); if (dentry) { audit_log_format(ab, " name="); audit_log_untrustedstring(ab, dentry->d_name.name); dput(dentry); } audit_log_format(ab, " dev="); audit_log_untrustedstring(ab, inode->i_sb->s_id); audit_log_format(ab, " ino=%lu", inode->i_ino); break; } case LSM_AUDIT_DATA_TASK: { struct task_struct *tsk = a->u.tsk; if (tsk) { pid_t pid = task_pid_nr(tsk); if (pid) { char comm[sizeof(tsk->comm)]; audit_log_format(ab, " opid=%d ocomm=", pid); audit_log_untrustedstring(ab, memcpy(comm, tsk->comm, sizeof(comm))); } } break; } case LSM_AUDIT_DATA_NET: if (a->u.net->sk) { struct sock *sk = a->u.net->sk; struct unix_sock *u; int len = 0; char *p = NULL; switch (sk->sk_family) { case AF_INET: { struct inet_sock *inet = inet_sk(sk); print_ipv4_addr(ab, inet->inet_rcv_saddr, inet->inet_sport, "laddr", "lport"); print_ipv4_addr(ab, inet->inet_daddr, inet->inet_dport, "faddr", "fport"); break; } #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: { struct inet_sock *inet = inet_sk(sk); print_ipv6_addr(ab, &sk->sk_v6_rcv_saddr, inet->inet_sport, "laddr", "lport"); print_ipv6_addr(ab, &sk->sk_v6_daddr, inet->inet_dport, "faddr", "fport"); break; } #endif case AF_UNIX: u = unix_sk(sk); if (u->path.dentry) { audit_log_d_path(ab, " path=", &u->path); break; } if (!u->addr) break; len = u->addr->len-sizeof(short); p = &u->addr->name->sun_path[0]; audit_log_format(ab, " path="); if (*p) audit_log_untrustedstring(ab, p); else audit_log_n_hex(ab, p, len); break; } } switch (a->u.net->family) { case AF_INET: print_ipv4_addr(ab, a->u.net->v4info.saddr, a->u.net->sport, "saddr", "src"); print_ipv4_addr(ab, a->u.net->v4info.daddr, a->u.net->dport, "daddr", "dest"); break; case AF_INET6: print_ipv6_addr(ab, &a->u.net->v6info.saddr, a->u.net->sport, "saddr", "src"); print_ipv6_addr(ab, &a->u.net->v6info.daddr, a->u.net->dport, "daddr", "dest"); break; } if (a->u.net->netif > 0) { struct net_device *dev; /* NOTE: we always use init's namespace */ dev = dev_get_by_index(&init_net, a->u.net->netif); if (dev) { audit_log_format(ab, " netif=%s", dev->name); dev_put(dev); } } break; #ifdef CONFIG_KEYS case LSM_AUDIT_DATA_KEY: audit_log_format(ab, " key_serial=%u", a->u.key_struct.key); if (a->u.key_struct.key_desc) { audit_log_format(ab, " key_desc="); audit_log_untrustedstring(ab, a->u.key_struct.key_desc); } break; #endif case LSM_AUDIT_DATA_KMOD: audit_log_format(ab, " kmod="); audit_log_untrustedstring(ab, a->u.kmod_name); break; } /* switch (a->type) */ }
/* * Code shared between mknod, mkdir, symlink and link */ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, struct inode *dir, struct dentry *entry, umode_t mode) { struct fuse_entry_out outarg; struct inode *inode; int err; struct fuse_forget_link *forget; forget = fuse_alloc_forget(); if (!forget) { fuse_put_request(fc, req); return -ENOMEM; } memset(&outarg, 0, sizeof(outarg)); req->in.h.nodeid = get_node_id(dir); req->out.numargs = 1; if (fc->minor < 9) req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; else req->out.args[0].size = sizeof(outarg); req->out.args[0].value = &outarg; fuse_request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); if (err) goto out_put_forget_req; err = -EIO; if (invalid_nodeid(outarg.nodeid)) goto out_put_forget_req; if ((outarg.attr.mode ^ mode) & S_IFMT) goto out_put_forget_req; inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, &outarg.attr, entry_attr_timeout(&outarg), 0); if (!inode) { fuse_queue_forget(fc, forget, outarg.nodeid, 1); return -ENOMEM; } kfree(forget); if (S_ISDIR(inode->i_mode)) { struct dentry *alias; mutex_lock(&fc->inst_mutex); alias = d_find_alias(inode); if (alias) { /* New directory must have moved since mkdir */ mutex_unlock(&fc->inst_mutex); dput(alias); iput(inode); return -EBUSY; } d_instantiate(entry, inode); mutex_unlock(&fc->inst_mutex); } else d_instantiate(entry, inode); fuse_change_entry_timeout(entry, &outarg); fuse_invalidate_attr(dir); return 0; out_put_forget_req: kfree(forget); return err; }