int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) { struct dnotify_struct *dn; struct dnotify_struct *odn; struct dnotify_struct **prev; struct inode *inode; fl_owner_t id = current->files; int error = 0; if ((arg & ~DN_MULTISHOT) == 0) { dnotify_flush(filp, id); return 0; } if (!dir_notify_enable) return -EINVAL; inode = filp->f_dentry->d_inode; if (!S_ISDIR(inode->i_mode)) return -ENOTDIR; dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL); if (dn == NULL) return -ENOMEM; spin_lock(&inode->i_lock); prev = &inode->i_dnotify; while ((odn = *prev) != NULL) { if ((odn->dn_owner == id) && (odn->dn_filp == filp)) { odn->dn_fd = fd; odn->dn_mask |= arg; inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; goto out_free; } prev = &odn->dn_next; } error = f_setown(filp, current->pid, 0); if (error) goto out_free; dn->dn_mask = arg; dn->dn_fd = fd; dn->dn_filp = filp; dn->dn_owner = id; inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; dn->dn_next = inode->i_dnotify; inode->i_dnotify = dn; spin_unlock(&inode->i_lock); if (filp->f_op && filp->f_op->dir_notify) return filp->f_op->dir_notify(filp, arg); return 0; out_free: spin_unlock(&inode->i_lock); kmem_cache_free(dn_cache, dn); return error; }
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file *filp) { long err = -EINVAL; switch (cmd) { case F_DUPFD: err = f_dupfd(arg, filp, 0); break; case F_DUPFD_CLOEXEC: err = f_dupfd(arg, filp, O_CLOEXEC); break; case F_GETFD: err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; break; case F_SETFD: err = 0; set_close_on_exec(fd, arg & FD_CLOEXEC); break; case F_GETFL: err = filp->f_flags; break; case F_SETFL: err = setfl(fd, filp, arg); break; #if BITS_PER_LONG != 32 /* 32-bit arches must use fcntl64() */ case F_OFD_GETLK: #endif case F_GETLK: err = fcntl_getlk(filp, cmd, (struct flock __user *) arg); break; #if BITS_PER_LONG != 32 /* 32-bit arches must use fcntl64() */ case F_OFD_SETLK: case F_OFD_SETLKW: #endif /* Fallthrough */ case F_SETLK: case F_SETLKW: err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg); break; case F_GETOWN: /* * XXX If f_owner is a process group, the * negative return value will get converted * into an error. Oops. If we keep the * current syscall conventions, the only way * to fix this will be in libc. */ err = f_getown(filp); force_successful_syscall_return(); break; case F_SETOWN: err = f_setown(filp, arg, 1); break; case F_GETOWN_EX: err = f_getown_ex(filp, arg); break; case F_SETOWN_EX: err = f_setown_ex(filp, arg); break; case F_GETOWNER_UIDS: err = f_getowner_uids(filp, arg); break; case F_GETSIG: err = filp->f_owner.signum; break; case F_SETSIG: /* arg == 0 restores default behaviour. */ if (!valid_signal(arg)) { break; } err = 0; filp->f_owner.signum = arg; break; case F_GETLEASE: err = fcntl_getlease(filp); break; case F_SETLEASE: err = fcntl_setlease(fd, filp, arg); break; case F_NOTIFY: err = fcntl_dirnotify(fd, filp, arg); break; case F_SETPIPE_SZ: case F_GETPIPE_SZ: err = pipe_fcntl(filp, cmd, arg); break; case F_ADD_SEALS: case F_GET_SEALS: err = shmem_fcntl(filp, cmd, arg); break; default: break; } return err; }
int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) { struct dnotify_struct *dn; struct dnotify_struct *odn; struct dnotify_struct **prev; struct inode *inode; fl_owner_t id = current->files; struct file *f; int error = 0; if ((arg & ~DN_MULTISHOT) == 0) { dnotify_flush(filp, id); return 0; } if (!dir_notify_enable) return -EINVAL; inode = filp->f_dentry->d_inode; if (!S_ISDIR(inode->i_mode)) return -ENOTDIR; dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL); if (dn == NULL) return -ENOMEM; spin_lock(&inode->i_lock); prev = &inode->i_dnotify; while ((odn = *prev) != NULL) { if ((odn->dn_owner == id) && (odn->dn_filp == filp)) { odn->dn_fd = fd; odn->dn_mask |= arg; inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; goto out_free; } prev = &odn->dn_next; } rcu_read_lock(); f = fcheck(fd); rcu_read_unlock(); /* we'd lost the race with close(), sod off silently */ /* note that inode->i_lock prevents reordering problems * between accesses to descriptor table and ->i_dnotify */ if (f != filp) goto out_free; error = f_setown(filp, current->pid, 0); if (error) goto out_free; dn->dn_mask = arg; dn->dn_fd = fd; dn->dn_filp = filp; dn->dn_owner = id; inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; dn->dn_next = inode->i_dnotify; inode->i_dnotify = dn; spin_unlock(&inode->i_lock); if (filp->f_op && filp->f_op->dir_notify) return filp->f_op->dir_notify(filp, arg); return 0; out_free: spin_unlock(&inode->i_lock); kmem_cache_free(dn_cache, dn); return error; }