void autofs_kill_sb(struct super_block *sb) { struct autofs_sb_info *sbi = autofs_sbi(sb); unsigned int n; /* * In the event of a failure in get_sb_nodev the superblock * info is not present so nothing else has been setup, so * just call kill_anon_super when we are called from * deactivate_super. */ if (!sbi) goto out_kill_sb; if (!sbi->catatonic) autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ put_pid(sbi->oz_pgrp); autofs_hash_nuke(sbi); for (n = 0; n < AUTOFS_MAX_SYMLINKS; n++) { if (test_bit(n, sbi->symlink_bitmap)) kfree(sbi->symlink[n].data); } kfree(sb->s_fs_info); out_kill_sb: DPRINTK(("autofs: shutting down\n")); kill_anon_super(sb); }
/* * NOTE! * * Normal filesystems would do a "d_delete()" to tell the VFS dcache * that the file no longer exists. However, doing that means that the * VFS layer can turn the dentry into a negative dentry, which we * obviously do not want (we're dropping the entry not because it * doesn't exist, but because it has timed out). * * Also see autofs_root_rmdir().. */ static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); struct autofs_dirhash *dh = &sbi->dirhash; struct autofs_dir_ent *ent; unsigned int n; /* This allows root to remove symlinks */ if ( !autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) return -EACCES; ent = autofs_hash_lookup(dh, &dentry->d_name); if ( !ent ) return -ENOENT; n = ent->ino - AUTOFS_FIRST_SYMLINK; if ( n >= AUTOFS_MAX_SYMLINKS ) return -EISDIR; /* It's a directory, dummy */ if ( !test_bit(n,sbi->symlink_bitmap) ) return -EINVAL; /* Nonexistent symlink? Shouldn't happen */ dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL; autofs_hash_delete(ent); clear_bit(n,sbi->symlink_bitmap); kfree(sbi->symlink[n].data); d_drop(dentry); return 0; }
static int autofs_root_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb); void __user *argp = (void __user *)arg; DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",cmd,arg,sbi,task_pgrp_nr(current))); if (_IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) || _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT) return -ENOTTY; if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN)) return -EPERM; switch(cmd) { case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */ return autofs_wait_release(sbi,(autofs_wqt_t)arg,0); case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */ return autofs_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT); case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */ autofs_catatonic_mode(sbi); return 0; case AUTOFS_IOC_PROTOVER: /* Get protocol version */ return autofs_get_protover(argp); case AUTOFS_IOC_SETTIMEOUT: return autofs_get_set_timeout(sbi, argp); case AUTOFS_IOC_EXPIRE: return autofs_expire_run(inode->i_sb, sbi, filp->f_path.mnt, argp); default: return -ENOSYS; } }
static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); struct autofs_dirhash *dh = &sbi->dirhash; struct autofs_dir_ent *ent; lock_kernel(); if (!autofs_oz_mode(sbi)) { unlock_kernel(); return -EACCES; } ent = autofs_hash_lookup(dh, &dentry->d_name); if (!ent) { unlock_kernel(); return -ENOENT; } if ((unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO) { unlock_kernel(); return -ENOTDIR; /* Not a directory */ } if (ent->dentry != dentry) { printk("autofs_rmdir: odentry != dentry for entry %s\n", dentry->d_name.name); } dentry->d_time = (unsigned long)(struct autofs_dir_ent *)NULL; autofs_hash_delete(ent); drop_nlink(dir); d_drop(dentry); unlock_kernel(); return 0; }
static int autofs_show_options(struct seq_file *m, struct dentry *root) { struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); struct inode *root_inode = d_inode(root->d_sb->s_root); if (!sbi) return 0; seq_printf(m, ",fd=%d", sbi->pipefd); if (!uid_eq(root_inode->i_uid, GLOBAL_ROOT_UID)) seq_printf(m, ",uid=%u", from_kuid_munged(&init_user_ns, root_inode->i_uid)); if (!gid_eq(root_inode->i_gid, GLOBAL_ROOT_GID)) seq_printf(m, ",gid=%u", from_kgid_munged(&init_user_ns, root_inode->i_gid)); seq_printf(m, ",pgrp=%d", pid_vnr(sbi->oz_pgrp)); seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ); seq_printf(m, ",minproto=%d", sbi->min_proto); seq_printf(m, ",maxproto=%d", sbi->max_proto); if (autofs_type_offset(sbi->type)) seq_printf(m, ",offset"); else if (autofs_type_direct(sbi->type)) seq_printf(m, ",direct"); else seq_printf(m, ",indirect"); #ifdef CONFIG_CHECKPOINT_RESTORE if (sbi->pipe) seq_printf(m, ",pipe_ino=%ld", file_inode(sbi->pipe)->i_ino); else seq_printf(m, ",pipe_ino=-1"); #endif return 0; }
static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); struct autofs_dirhash *dh = &sbi->dirhash; struct autofs_dir_ent *ent; struct inode *inode; ino_t ino; lock_kernel(); if (!autofs_oz_mode(sbi)) { unlock_kernel(); return -EACCES; } ent = autofs_hash_lookup(dh, &dentry->d_name); if (ent) { unlock_kernel(); return -EEXIST; } if (sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO) { printk("autofs: Out of inode numbers -- what the heck did you do??\n"); unlock_kernel(); return -ENOSPC; } ino = sbi->next_dir_ino++; ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); if (!ent) { unlock_kernel(); return -ENOSPC; } ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL); if (!ent->name) { kfree(ent); unlock_kernel(); return -ENOSPC; } ent->hash = dentry->d_name.hash; memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len)); ent->ino = ino; ent->dentry = dentry; autofs_hash_insert(dh,ent); inc_nlink(dir); inode = autofs_iget(dir->i_sb, ino); if (IS_ERR(inode)) { drop_nlink(dir); return PTR_ERR(inode); } d_instantiate(dentry, inode); unlock_kernel(); return 0; }
static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); struct autofs_dirhash *dh = &sbi->dirhash; struct autofs_dir_ent *ent; unsigned int n; int slsize; struct autofs_symlink *sl; DPRINTK(("autofs_root_symlink: %s <- ", symname)); autofs_say(dentry->d_name.name,dentry->d_name.len); if ( !autofs_oz_mode(sbi) ) return -EACCES; if ( autofs_hash_lookup(dh, &dentry->d_name) ) return -EEXIST; n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS); if ( n >= AUTOFS_MAX_SYMLINKS ) return -ENOSPC; set_bit(n,sbi->symlink_bitmap); sl = &sbi->symlink[n]; sl->len = strlen(symname); sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL); if ( !sl->data ) { clear_bit(n,sbi->symlink_bitmap); return -ENOSPC; } ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); if ( !ent ) { kfree(sl->data); clear_bit(n,sbi->symlink_bitmap); return -ENOSPC; } ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL); if ( !ent->name ) { kfree(sl->data); kfree(ent); clear_bit(n,sbi->symlink_bitmap); return -ENOSPC; } memcpy(sl->data,symname,slsize); sl->mtime = CURRENT_TIME; ent->ino = AUTOFS_FIRST_SYMLINK + n; ent->hash = dentry->d_name.hash; memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len)); ent->dentry = NULL; /* We don't keep the dentry for symlinks */ autofs_hash_insert(dh,ent); d_instantiate(dentry, iget(dir->i_sb,ent->ino)); return 0; }
static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi; int oz_mode; DPRINTK(("autofs_root_lookup: name = ")); autofs_say(dentry->d_name.name,dentry->d_name.len); if (dentry->d_name.len > NAME_MAX) return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ sbi = autofs_sbi(dir->i_sb); oz_mode = autofs_oz_mode(sbi); DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode)); /* * Mark the dentry incomplete, but add it. This is needed so * that the VFS layer knows about the dentry, and we can count * on catching any lookups through the revalidate. * * Let all the hard work be done by the revalidate function that * needs to be able to do this anyway.. * * We need to do this before we release the directory semaphore. */ dentry->d_op = &autofs_dentry_operations; dentry->d_flags |= DCACHE_AUTOFS_PENDING; d_add(dentry, NULL); up(&dir->i_sem); autofs_revalidate(dentry, 0); down(&dir->i_sem); /* * If we are still pending, check if we had to handle * a signal. If so we can force a restart.. */ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { if (signal_pending(current)) return ERR_PTR(-ERESTARTNOINTR); } /* * If this dentry is unhashed, then we shouldn't honour this * lookup even if the dentry is positive. Returning ENOENT here * doesn't do the right thing for all system calls, but it should * be OK for the operations we permit from an autofs. */ if ( dentry->d_inode && d_unhashed(dentry) ) return ERR_PTR(-ENOENT); return NULL; }
struct inode *autofs_iget(struct super_block *sb, unsigned long ino) { unsigned int n; struct autofs_sb_info *sbi = autofs_sbi(sb); struct inode *inode; inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; /* Initialize to the default case (stub directory) */ inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; inode->i_nlink = 2; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; if (ino == AUTOFS_ROOT_INO) { inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; inode->i_op = &autofs_root_inode_operations; inode->i_fop = &autofs_root_operations; goto done; } inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; if (ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO) { /* Symlink inode - should be in symlink list */ struct autofs_symlink *sl; n = ino - AUTOFS_FIRST_SYMLINK; if (n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) { printk("autofs: Looking for bad symlink inode %u\n", (unsigned int) ino); goto done; } inode->i_op = &autofs_symlink_inode_operations; sl = &sbi->symlink[n]; inode->i_private = sl; inode->i_mode = S_IFLNK | S_IRWXUGO; inode->i_mtime.tv_sec = inode->i_ctime.tv_sec = sl->mtime; inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0; inode->i_size = sl->len; inode->i_nlink = 1; } done: unlock_new_inode(inode); return inode; }
static const char *autofs_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { struct autofs_sb_info *sbi; struct autofs_info *ino; if (!dentry) return ERR_PTR(-ECHILD); sbi = autofs_sbi(dentry->d_sb); ino = autofs_dentry_ino(dentry); if (ino && !autofs_oz_mode(sbi)) ino->last_used = jiffies; return d_inode(dentry)->i_private; }
static void autofs_read_inode(struct inode *inode) { ino_t ino = inode->i_ino; unsigned int n; struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb); /* Initialize to the default case (stub directory) */ inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; inode->i_nlink = 2; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_blocks = 0; inode->i_blksize = 1024; if ( ino == AUTOFS_ROOT_INO ) { inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; inode->i_op = &autofs_root_inode_operations; inode->i_fop = &autofs_root_operations; inode->i_uid = inode->i_gid = 0; /* Changed in read_super */ return; } inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) { /* Symlink inode - should be in symlink list */ struct autofs_symlink *sl; n = ino - AUTOFS_FIRST_SYMLINK; if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) { printk("autofs: Looking for bad symlink inode %u\n", (unsigned int) ino); return; } inode->i_op = &autofs_symlink_inode_operations; sl = &sbi->symlink[n]; inode->u.generic_ip = sl; inode->i_mode = S_IFLNK | S_IRWXUGO; inode->i_mtime.tv_sec = inode->i_ctime.tv_sec = sl->mtime; inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0; inode->i_size = sl->len; inode->i_nlink = 1; } }
static int autofs_revalidate(struct dentry * dentry, struct nameidata *nd) { struct inode * dir; struct autofs_sb_info *sbi; struct autofs_dir_ent *ent; int res; lock_kernel(); dir = dentry->d_parent->d_inode; sbi = autofs_sbi(dir->i_sb); /* Pending dentry */ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { if (autofs_oz_mode(sbi)) res = 1; else res = try_to_fill_dentry(dentry, dir->i_sb, sbi); unlock_kernel(); return res; } /* Negative dentry.. invalidate if "old" */ if (!dentry->d_inode) { unlock_kernel(); return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); } /* Check for a non-mountpoint directory */ if (S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry)) { if (autofs_oz_mode(sbi)) res = 1; else res = try_to_fill_dentry(dentry, dir->i_sb, sbi); unlock_kernel(); return res; } /* Update the usage list */ if (!autofs_oz_mode(sbi)) { ent = (struct autofs_dir_ent *) dentry->d_time; if (ent) autofs_update_usage(&sbi->dirhash,ent); } unlock_kernel(); return 1; }
static void autofs_put_super(struct super_block *sb) { struct autofs_sb_info *sbi = autofs_sbi(sb); unsigned int n; if ( !sbi->catatonic ) autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ autofs_hash_nuke(&sbi->dirhash); for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) { if ( test_bit(n, sbi->symlink_bitmap) ) kfree(sbi->symlink[n].data); } kfree(sb->u.generic_sbp); DPRINTK(("autofs: shutting down\n")); }
static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct autofs_dir_ent *ent = NULL; struct autofs_dirhash *dirhash; struct autofs_sb_info *sbi; struct inode * inode = filp->f_path.dentry->d_inode; off_t onr, nr; lock_kernel(); sbi = autofs_sbi(inode->i_sb); dirhash = &sbi->dirhash; nr = filp->f_pos; switch(nr) { case 0: if (filldir(dirent, ".", 1, nr, inode->i_ino, DT_DIR) < 0) goto out; filp->f_pos = ++nr; /* fall through */ case 1: if (filldir(dirent, "..", 2, nr, inode->i_ino, DT_DIR) < 0) goto out; filp->f_pos = ++nr; /* fall through */ default: while (onr = nr, ent = autofs_hash_enum(dirhash,&nr,ent)) { if (!ent->dentry || d_mountpoint(ent->dentry)) { if (filldir(dirent,ent->name,ent->len,onr,ent->ino,DT_UNKNOWN) < 0) goto out; filp->f_pos = nr; } } break; } out: unlock_kernel(); return 0; }
void autofs_kill_sb(struct super_block *sb) { struct autofs_sb_info *sbi = autofs_sbi(sb); /* * In the event of a failure in get_sb_nodev the superblock * info is not present so nothing else has been setup, so * just call kill_anon_super when we are called from * deactivate_super. */ if (sbi) { /* Free wait queues, close pipe */ autofs_catatonic_mode(sbi); put_pid(sbi->oz_pgrp); } pr_debug("shutting down\n"); kill_litter_super(sb); if (sbi) kfree_rcu(sbi, rcu); }
/* ioctl dispatcher */ static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) { struct autofs_dev_ioctl *param; struct file *fp; struct autofs_sb_info *sbi; unsigned int cmd_first, cmd; ioctl_fn fn = NULL; int err = 0; cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); cmd = _IOC_NR(command); if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || cmd - cmd_first > AUTOFS_DEV_IOCTL_IOC_COUNT) { return -ENOTTY; } /* Only root can use ioctls other than AUTOFS_DEV_IOCTL_VERSION_CMD * and AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD */ if (cmd != AUTOFS_DEV_IOCTL_VERSION_CMD && cmd != AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD && !capable(CAP_SYS_ADMIN)) return -EPERM; /* Copy the parameters into kernel space. */ param = copy_dev_ioctl(user); if (IS_ERR(param)) return PTR_ERR(param); err = validate_dev_ioctl(command, param); if (err) goto out; fn = lookup_dev_ioctl(cmd); if (!fn) { pr_warn("unknown command 0x%08x\n", command); err = -ENOTTY; goto out; } fp = NULL; sbi = NULL; /* * For obvious reasons the openmount can't have a file * descriptor yet. We don't take a reference to the * file during close to allow for immediate release, * and the same for retrieving ioctl version. */ if (cmd != AUTOFS_DEV_IOCTL_VERSION_CMD && cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { struct super_block *sb; fp = fget(param->ioctlfd); if (!fp) { if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) goto cont; err = -EBADF; goto out; } sb = file_inode(fp)->i_sb; if (sb->s_type != &autofs_fs_type) { err = -EINVAL; fput(fp); goto out; } sbi = autofs_sbi(sb); /* * Admin needs to be able to set the mount catatonic in * order to be able to perform the re-open. */ if (!autofs_oz_mode(sbi) && cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { err = -EACCES; fput(fp); goto out; } } cont: err = fn(fp, sbi, param); if (fp) fput(fp); if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) err = -EFAULT; out: free_dev_ioctl(param); return err; }