Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
7
fs/autofs4/Makefile
Normal file
7
fs/autofs4/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for the linux autofs-filesystem routines.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AUTOFS4_FS) += autofs4.o
|
||||
|
||||
autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o
|
193
fs/autofs4/autofs_i.h
Normal file
193
fs/autofs4/autofs_i.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/* -*- c -*- ------------------------------------------------------------- *
|
||||
*
|
||||
* linux/fs/autofs/autofs_i.h
|
||||
*
|
||||
* Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
|
||||
*
|
||||
* This file is part of the Linux kernel and is made available under
|
||||
* the terms of the GNU General Public License, version 2, or at your
|
||||
* option, any later version, incorporated herein by reference.
|
||||
*
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/* Internal header file for autofs */
|
||||
|
||||
#include <linux/auto_fs4.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* This is the range of ioctl() numbers we claim as ours */
|
||||
#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY
|
||||
#define AUTOFS_IOC_COUNT 32
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <asm/current.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0)
|
||||
#else
|
||||
#define DPRINTK(fmt,args...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define AUTOFS_SUPER_MAGIC 0x0187
|
||||
|
||||
/*
|
||||
* If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the
|
||||
* kernel will keep the negative response cached for up to the time given
|
||||
* here, although the time can be shorter if the kernel throws the dcache
|
||||
* entry away. This probably should be settable from user space.
|
||||
*/
|
||||
#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */
|
||||
|
||||
/* Unified info structure. This is pointed to by both the dentry and
|
||||
inode structures. Each file in the filesystem has an instance of this
|
||||
structure. It holds a reference to the dentry, so dentries are never
|
||||
flushed while the file exists. All name lookups are dealt with at the
|
||||
dentry level, although the filesystem can interfere in the validation
|
||||
process. Readdir is implemented by traversing the dentry lists. */
|
||||
struct autofs_info {
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
|
||||
int flags;
|
||||
|
||||
struct autofs_sb_info *sbi;
|
||||
unsigned long last_used;
|
||||
|
||||
mode_t mode;
|
||||
size_t size;
|
||||
|
||||
void (*free)(struct autofs_info *);
|
||||
union {
|
||||
const char *symlink;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
|
||||
|
||||
struct autofs_wait_queue {
|
||||
wait_queue_head_t queue;
|
||||
struct autofs_wait_queue *next;
|
||||
autofs_wqt_t wait_queue_token;
|
||||
/* We use the following to see what we are waiting for */
|
||||
int hash;
|
||||
int len;
|
||||
char *name;
|
||||
/* This is for status reporting upon return */
|
||||
int status;
|
||||
atomic_t wait_ctr;
|
||||
};
|
||||
|
||||
#define AUTOFS_SBI_MAGIC 0x6d4a556d
|
||||
|
||||
struct autofs_sb_info {
|
||||
u32 magic;
|
||||
struct file *pipe;
|
||||
pid_t oz_pgrp;
|
||||
int catatonic;
|
||||
int version;
|
||||
int sub_version;
|
||||
unsigned long exp_timeout;
|
||||
int reghost_enabled;
|
||||
int needs_reghost;
|
||||
struct super_block *sb;
|
||||
struct semaphore wq_sem;
|
||||
struct autofs_wait_queue *queues; /* Wait queue pointer */
|
||||
};
|
||||
|
||||
static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb)
|
||||
{
|
||||
return (struct autofs_sb_info *)(sb->s_fs_info);
|
||||
}
|
||||
|
||||
static inline struct autofs_info *autofs4_dentry_ino(struct dentry *dentry)
|
||||
{
|
||||
return (struct autofs_info *)(dentry->d_fsdata);
|
||||
}
|
||||
|
||||
/* autofs4_oz_mode(): do we see the man behind the curtain? (The
|
||||
processes which do manipulations for us in user space sees the raw
|
||||
filesystem without "magic".) */
|
||||
|
||||
static inline int autofs4_oz_mode(struct autofs_sb_info *sbi) {
|
||||
return sbi->catatonic || process_group(current) == sbi->oz_pgrp;
|
||||
}
|
||||
|
||||
/* Does a dentry have some pending activity? */
|
||||
static inline int autofs4_ispending(struct dentry *dentry)
|
||||
{
|
||||
struct autofs_info *inf = autofs4_dentry_ino(dentry);
|
||||
|
||||
return (dentry->d_flags & DCACHE_AUTOFS_PENDING) ||
|
||||
(inf != NULL && inf->flags & AUTOFS_INF_EXPIRING);
|
||||
}
|
||||
|
||||
static inline void autofs4_copy_atime(struct file *src, struct file *dst)
|
||||
{
|
||||
dst->f_dentry->d_inode->i_atime = src->f_dentry->d_inode->i_atime;
|
||||
return;
|
||||
}
|
||||
|
||||
struct inode *autofs4_get_inode(struct super_block *, struct autofs_info *);
|
||||
void autofs4_free_ino(struct autofs_info *);
|
||||
|
||||
/* Expiration */
|
||||
int is_autofs4_dentry(struct dentry *);
|
||||
int autofs4_expire_run(struct super_block *, struct vfsmount *,
|
||||
struct autofs_sb_info *,
|
||||
struct autofs_packet_expire __user *);
|
||||
int autofs4_expire_multi(struct super_block *, struct vfsmount *,
|
||||
struct autofs_sb_info *, int __user *);
|
||||
|
||||
/* Operations structures */
|
||||
|
||||
extern struct inode_operations autofs4_symlink_inode_operations;
|
||||
extern struct inode_operations autofs4_dir_inode_operations;
|
||||
extern struct inode_operations autofs4_root_inode_operations;
|
||||
extern struct file_operations autofs4_dir_operations;
|
||||
extern struct file_operations autofs4_root_operations;
|
||||
|
||||
/* Initializing function */
|
||||
|
||||
int autofs4_fill_super(struct super_block *, void *, int);
|
||||
struct autofs_info *autofs4_init_ino(struct autofs_info *, struct autofs_sb_info *sbi, mode_t mode);
|
||||
|
||||
/* Queue management functions */
|
||||
|
||||
enum autofs_notify
|
||||
{
|
||||
NFY_NONE,
|
||||
NFY_MOUNT,
|
||||
NFY_EXPIRE
|
||||
};
|
||||
|
||||
int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify);
|
||||
int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
|
||||
void autofs4_catatonic_mode(struct autofs_sb_info *);
|
||||
|
||||
static inline int simple_positive(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_inode && !d_unhashed(dentry);
|
||||
}
|
||||
|
||||
static inline int simple_empty_nolock(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *child;
|
||||
int ret = 0;
|
||||
|
||||
list_for_each_entry(child, &dentry->d_subdirs, d_child)
|
||||
if (simple_positive(child))
|
||||
goto out;
|
||||
ret = 1;
|
||||
out:
|
||||
return ret;
|
||||
}
|
358
fs/autofs4/expire.c
Normal file
358
fs/autofs4/expire.c
Normal file
@@ -0,0 +1,358 @@
|
||||
/* -*- c -*- --------------------------------------------------------------- *
|
||||
*
|
||||
* linux/fs/autofs/expire.c
|
||||
*
|
||||
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
|
||||
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
|
||||
* Copyright 2001-2003 Ian Kent <raven@themaw.net>
|
||||
*
|
||||
* This file is part of the Linux kernel and is made available under
|
||||
* the terms of the GNU General Public License, version 2, or at your
|
||||
* option, any later version, incorporated herein by reference.
|
||||
*
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#include "autofs_i.h"
|
||||
|
||||
static unsigned long now;
|
||||
|
||||
/* Check if a dentry can be expired return 1 if it can else return 0 */
|
||||
static inline int autofs4_can_expire(struct dentry *dentry,
|
||||
unsigned long timeout, int do_now)
|
||||
{
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
|
||||
/* dentry in the process of being deleted */
|
||||
if (ino == NULL)
|
||||
return 0;
|
||||
|
||||
/* No point expiring a pending mount */
|
||||
if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
|
||||
return 0;
|
||||
|
||||
if (!do_now) {
|
||||
/* Too young to die */
|
||||
if (time_after(ino->last_used + timeout, now))
|
||||
return 0;
|
||||
|
||||
/* update last_used here :-
|
||||
- obviously makes sense if it is in use now
|
||||
- less obviously, prevents rapid-fire expire
|
||||
attempts if expire fails the first time */
|
||||
ino->last_used = now;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check a mount point for busyness return 1 if not busy, otherwise */
|
||||
static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
DPRINTK("dentry %p %.*s",
|
||||
dentry, (int)dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
mntget(mnt);
|
||||
dget(dentry);
|
||||
|
||||
if (!follow_down(&mnt, &dentry))
|
||||
goto done;
|
||||
|
||||
while (d_mountpoint(dentry) && follow_down(&mnt, &dentry))
|
||||
;
|
||||
|
||||
/* This is an autofs submount, we can't expire it */
|
||||
if (is_autofs4_dentry(dentry))
|
||||
goto done;
|
||||
|
||||
/* The big question */
|
||||
if (may_umount_tree(mnt) == 0)
|
||||
status = 1;
|
||||
done:
|
||||
DPRINTK("returning = %d", status);
|
||||
mntput(mnt);
|
||||
dput(dentry);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Check a directory tree of mount points for busyness
|
||||
* The tree is not busy iff no mountpoints are busy
|
||||
* Return 1 if the tree is busy or 0 otherwise
|
||||
*/
|
||||
static int autofs4_check_tree(struct vfsmount *mnt,
|
||||
struct dentry *top,
|
||||
unsigned long timeout,
|
||||
int do_now)
|
||||
{
|
||||
struct dentry *this_parent = top;
|
||||
struct list_head *next;
|
||||
|
||||
DPRINTK("parent %p %.*s",
|
||||
top, (int)top->d_name.len, top->d_name.name);
|
||||
|
||||
/* Negative dentry - give up */
|
||||
if (!simple_positive(top))
|
||||
return 0;
|
||||
|
||||
/* Timeout of a tree mount is determined by its top dentry */
|
||||
if (!autofs4_can_expire(top, timeout, do_now))
|
||||
return 0;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
repeat:
|
||||
next = this_parent->d_subdirs.next;
|
||||
resume:
|
||||
while (next != &this_parent->d_subdirs) {
|
||||
struct dentry *dentry = list_entry(next, struct dentry, d_child);
|
||||
|
||||
/* Negative dentry - give up */
|
||||
if (!simple_positive(dentry)) {
|
||||
next = next->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
DPRINTK("dentry %p %.*s",
|
||||
dentry, (int)dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
if (!simple_empty_nolock(dentry)) {
|
||||
this_parent = dentry;
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
dentry = dget(dentry);
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
if (d_mountpoint(dentry)) {
|
||||
/* First busy => tree busy */
|
||||
if (!autofs4_check_mount(mnt, dentry)) {
|
||||
dput(dentry);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dput(dentry);
|
||||
spin_lock(&dcache_lock);
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
if (this_parent != top) {
|
||||
next = this_parent->d_child.next;
|
||||
this_parent = this_parent->d_parent;
|
||||
goto resume;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dentry *autofs4_check_leaves(struct vfsmount *mnt,
|
||||
struct dentry *parent,
|
||||
unsigned long timeout,
|
||||
int do_now)
|
||||
{
|
||||
struct dentry *this_parent = parent;
|
||||
struct list_head *next;
|
||||
|
||||
DPRINTK("parent %p %.*s",
|
||||
parent, (int)parent->d_name.len, parent->d_name.name);
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
repeat:
|
||||
next = this_parent->d_subdirs.next;
|
||||
resume:
|
||||
while (next != &this_parent->d_subdirs) {
|
||||
struct dentry *dentry = list_entry(next, struct dentry, d_child);
|
||||
|
||||
/* Negative dentry - give up */
|
||||
if (!simple_positive(dentry)) {
|
||||
next = next->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
DPRINTK("dentry %p %.*s",
|
||||
dentry, (int)dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
if (!list_empty(&dentry->d_subdirs)) {
|
||||
this_parent = dentry;
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
dentry = dget(dentry);
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
if (d_mountpoint(dentry)) {
|
||||
/* Can we expire this guy */
|
||||
if (!autofs4_can_expire(dentry, timeout, do_now))
|
||||
goto cont;
|
||||
|
||||
/* Can we umount this guy */
|
||||
if (autofs4_check_mount(mnt, dentry))
|
||||
return dentry;
|
||||
|
||||
}
|
||||
cont:
|
||||
dput(dentry);
|
||||
spin_lock(&dcache_lock);
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
if (this_parent != parent) {
|
||||
next = this_parent->d_child.next;
|
||||
this_parent = this_parent->d_parent;
|
||||
goto resume;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an eligible tree to time-out
|
||||
* A tree is eligible if :-
|
||||
* - it is unused by any user process
|
||||
* - it has been unused for exp_timeout time
|
||||
*/
|
||||
static struct dentry *autofs4_expire(struct super_block *sb,
|
||||
struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi,
|
||||
int how)
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct dentry *root = sb->s_root;
|
||||
struct dentry *expired = NULL;
|
||||
struct list_head *next;
|
||||
int do_now = how & AUTOFS_EXP_IMMEDIATE;
|
||||
int exp_leaves = how & AUTOFS_EXP_LEAVES;
|
||||
|
||||
if ( !sbi->exp_timeout || !root )
|
||||
return NULL;
|
||||
|
||||
now = jiffies;
|
||||
timeout = sbi->exp_timeout;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
next = root->d_subdirs.next;
|
||||
|
||||
/* On exit from the loop expire is set to a dgot dentry
|
||||
* to expire or it's NULL */
|
||||
while ( next != &root->d_subdirs ) {
|
||||
struct dentry *dentry = list_entry(next, struct dentry, d_child);
|
||||
|
||||
/* Negative dentry - give up */
|
||||
if ( !simple_positive(dentry) ) {
|
||||
next = next->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
dentry = dget(dentry);
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
/* Case 1: indirect mount or top level direct mount */
|
||||
if (d_mountpoint(dentry)) {
|
||||
DPRINTK("checking mountpoint %p %.*s",
|
||||
dentry, (int)dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
/* Can we expire this guy */
|
||||
if (!autofs4_can_expire(dentry, timeout, do_now))
|
||||
goto next;
|
||||
|
||||
/* Can we umount this guy */
|
||||
if (autofs4_check_mount(mnt, dentry)) {
|
||||
expired = dentry;
|
||||
break;
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
|
||||
if ( simple_empty(dentry) )
|
||||
goto next;
|
||||
|
||||
/* Case 2: tree mount, expire iff entire tree is not busy */
|
||||
if (!exp_leaves) {
|
||||
if (autofs4_check_tree(mnt, dentry, timeout, do_now)) {
|
||||
expired = dentry;
|
||||
break;
|
||||
}
|
||||
/* Case 3: direct mount, expire individual leaves */
|
||||
} else {
|
||||
expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
|
||||
if (expired) {
|
||||
dput(dentry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
next:
|
||||
dput(dentry);
|
||||
spin_lock(&dcache_lock);
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
if ( expired ) {
|
||||
DPRINTK("returning %p %.*s",
|
||||
expired, (int)expired->d_name.len, expired->d_name.name);
|
||||
spin_lock(&dcache_lock);
|
||||
list_del(&expired->d_parent->d_subdirs);
|
||||
list_add(&expired->d_parent->d_subdirs, &expired->d_child);
|
||||
spin_unlock(&dcache_lock);
|
||||
return expired;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Perform an expiry operation */
|
||||
int autofs4_expire_run(struct super_block *sb,
|
||||
struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi,
|
||||
struct autofs_packet_expire __user *pkt_p)
|
||||
{
|
||||
struct autofs_packet_expire pkt;
|
||||
struct dentry *dentry;
|
||||
|
||||
memset(&pkt,0,sizeof pkt);
|
||||
|
||||
pkt.hdr.proto_version = sbi->version;
|
||||
pkt.hdr.type = autofs_ptype_expire;
|
||||
|
||||
if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL)
|
||||
return -EAGAIN;
|
||||
|
||||
pkt.len = dentry->d_name.len;
|
||||
memcpy(pkt.name, dentry->d_name.name, pkt.len);
|
||||
pkt.name[pkt.len] = '\0';
|
||||
dput(dentry);
|
||||
|
||||
if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call repeatedly until it returns -EAGAIN, meaning there's nothing
|
||||
more to be done */
|
||||
int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi, int __user *arg)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
int ret = -EAGAIN;
|
||||
int do_now = 0;
|
||||
|
||||
if (arg && get_user(do_now, arg))
|
||||
return -EFAULT;
|
||||
|
||||
if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) {
|
||||
struct autofs_info *de_info = autofs4_dentry_ino(dentry);
|
||||
|
||||
/* This is synchronous because it makes the daemon a
|
||||
little easier */
|
||||
de_info->flags |= AUTOFS_INF_EXPIRING;
|
||||
ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
|
||||
de_info->flags &= ~AUTOFS_INF_EXPIRING;
|
||||
dput(dentry);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
42
fs/autofs4/init.c
Normal file
42
fs/autofs4/init.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/* -*- c -*- --------------------------------------------------------------- *
|
||||
*
|
||||
* linux/fs/autofs/init.c
|
||||
*
|
||||
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
|
||||
*
|
||||
* This file is part of the Linux kernel and is made available under
|
||||
* the terms of the GNU General Public License, version 2, or at your
|
||||
* option, any later version, incorporated herein by reference.
|
||||
*
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include "autofs_i.h"
|
||||
|
||||
static struct super_block *autofs_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
return get_sb_nodev(fs_type, flags, data, autofs4_fill_super);
|
||||
}
|
||||
|
||||
static struct file_system_type autofs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "autofs",
|
||||
.get_sb = autofs_get_sb,
|
||||
.kill_sb = kill_anon_super,
|
||||
};
|
||||
|
||||
static int __init init_autofs4_fs(void)
|
||||
{
|
||||
return register_filesystem(&autofs_fs_type);
|
||||
}
|
||||
|
||||
static void __exit exit_autofs4_fs(void)
|
||||
{
|
||||
unregister_filesystem(&autofs_fs_type);
|
||||
}
|
||||
|
||||
module_init(init_autofs4_fs)
|
||||
module_exit(exit_autofs4_fs)
|
||||
MODULE_LICENSE("GPL");
|
324
fs/autofs4/inode.c
Normal file
324
fs/autofs4/inode.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/* -*- c -*- --------------------------------------------------------------- *
|
||||
*
|
||||
* linux/fs/autofs/inode.c
|
||||
*
|
||||
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
|
||||
*
|
||||
* This file is part of the Linux kernel and is made available under
|
||||
* the terms of the GNU General Public License, version 2, or at your
|
||||
* option, any later version, incorporated herein by reference.
|
||||
*
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "autofs_i.h"
|
||||
#include <linux/module.h>
|
||||
|
||||
static void ino_lnkfree(struct autofs_info *ino)
|
||||
{
|
||||
if (ino->u.symlink) {
|
||||
kfree(ino->u.symlink);
|
||||
ino->u.symlink = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
|
||||
struct autofs_sb_info *sbi, mode_t mode)
|
||||
{
|
||||
int reinit = 1;
|
||||
|
||||
if (ino == NULL) {
|
||||
reinit = 0;
|
||||
ino = kmalloc(sizeof(*ino), GFP_KERNEL);
|
||||
}
|
||||
|
||||
if (ino == NULL)
|
||||
return NULL;
|
||||
|
||||
ino->flags = 0;
|
||||
ino->mode = mode;
|
||||
ino->inode = NULL;
|
||||
ino->dentry = NULL;
|
||||
ino->size = 0;
|
||||
|
||||
ino->last_used = jiffies;
|
||||
|
||||
ino->sbi = sbi;
|
||||
|
||||
if (reinit && ino->free)
|
||||
(ino->free)(ino);
|
||||
|
||||
memset(&ino->u, 0, sizeof(ino->u));
|
||||
|
||||
ino->free = NULL;
|
||||
|
||||
if (S_ISLNK(mode))
|
||||
ino->free = ino_lnkfree;
|
||||
|
||||
return ino;
|
||||
}
|
||||
|
||||
void autofs4_free_ino(struct autofs_info *ino)
|
||||
{
|
||||
if (ino->dentry) {
|
||||
ino->dentry->d_fsdata = NULL;
|
||||
if (ino->dentry->d_inode)
|
||||
dput(ino->dentry);
|
||||
ino->dentry = NULL;
|
||||
}
|
||||
if (ino->free)
|
||||
(ino->free)(ino);
|
||||
kfree(ino);
|
||||
}
|
||||
|
||||
static void autofs4_put_super(struct super_block *sb)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(sb);
|
||||
|
||||
sb->s_fs_info = NULL;
|
||||
|
||||
if ( !sbi->catatonic )
|
||||
autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */
|
||||
|
||||
kfree(sbi);
|
||||
|
||||
DPRINTK("shutting down");
|
||||
}
|
||||
|
||||
static struct super_operations autofs4_sops = {
|
||||
.put_super = autofs4_put_super,
|
||||
.statfs = simple_statfs,
|
||||
};
|
||||
|
||||
enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto};
|
||||
|
||||
static match_table_t tokens = {
|
||||
{Opt_fd, "fd=%u"},
|
||||
{Opt_uid, "uid=%u"},
|
||||
{Opt_gid, "gid=%u"},
|
||||
{Opt_pgrp, "pgrp=%u"},
|
||||
{Opt_minproto, "minproto=%u"},
|
||||
{Opt_maxproto, "maxproto=%u"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
|
||||
pid_t *pgrp, int *minproto, int *maxproto)
|
||||
{
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int option;
|
||||
|
||||
*uid = current->uid;
|
||||
*gid = current->gid;
|
||||
*pgrp = process_group(current);
|
||||
|
||||
*minproto = AUTOFS_MIN_PROTO_VERSION;
|
||||
*maxproto = AUTOFS_MAX_PROTO_VERSION;
|
||||
|
||||
*pipefd = -1;
|
||||
|
||||
if (!options)
|
||||
return 1;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case Opt_fd:
|
||||
if (match_int(args, pipefd))
|
||||
return 1;
|
||||
break;
|
||||
case Opt_uid:
|
||||
if (match_int(args, &option))
|
||||
return 1;
|
||||
*uid = option;
|
||||
break;
|
||||
case Opt_gid:
|
||||
if (match_int(args, &option))
|
||||
return 1;
|
||||
*gid = option;
|
||||
break;
|
||||
case Opt_pgrp:
|
||||
if (match_int(args, &option))
|
||||
return 1;
|
||||
*pgrp = option;
|
||||
break;
|
||||
case Opt_minproto:
|
||||
if (match_int(args, &option))
|
||||
return 1;
|
||||
*minproto = option;
|
||||
break;
|
||||
case Opt_maxproto:
|
||||
if (match_int(args, &option))
|
||||
return 1;
|
||||
*maxproto = option;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return (*pipefd < 0);
|
||||
}
|
||||
|
||||
static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
|
||||
{
|
||||
struct autofs_info *ino;
|
||||
|
||||
ino = autofs4_init_ino(NULL, sbi, S_IFDIR | 0755);
|
||||
if (!ino)
|
||||
return NULL;
|
||||
|
||||
return ino;
|
||||
}
|
||||
|
||||
int autofs4_fill_super(struct super_block *s, void *data, int silent)
|
||||
{
|
||||
struct inode * root_inode;
|
||||
struct dentry * root;
|
||||
struct file * pipe;
|
||||
int pipefd;
|
||||
struct autofs_sb_info *sbi;
|
||||
struct autofs_info *ino;
|
||||
int minproto, maxproto;
|
||||
|
||||
sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL);
|
||||
if ( !sbi )
|
||||
goto fail_unlock;
|
||||
DPRINTK("starting up, sbi = %p",sbi);
|
||||
|
||||
memset(sbi, 0, sizeof(*sbi));
|
||||
|
||||
s->s_fs_info = sbi;
|
||||
sbi->magic = AUTOFS_SBI_MAGIC;
|
||||
sbi->catatonic = 0;
|
||||
sbi->exp_timeout = 0;
|
||||
sbi->oz_pgrp = process_group(current);
|
||||
sbi->sb = s;
|
||||
sbi->version = 0;
|
||||
sbi->sub_version = 0;
|
||||
init_MUTEX(&sbi->wq_sem);
|
||||
sbi->queues = NULL;
|
||||
s->s_blocksize = 1024;
|
||||
s->s_blocksize_bits = 10;
|
||||
s->s_magic = AUTOFS_SUPER_MAGIC;
|
||||
s->s_op = &autofs4_sops;
|
||||
s->s_time_gran = 1;
|
||||
|
||||
/*
|
||||
* Get the root inode and dentry, but defer checking for errors.
|
||||
*/
|
||||
ino = autofs4_mkroot(sbi);
|
||||
if (!ino)
|
||||
goto fail_free;
|
||||
root_inode = autofs4_get_inode(s, ino);
|
||||
kfree(ino);
|
||||
if (!root_inode)
|
||||
goto fail_free;
|
||||
|
||||
root_inode->i_op = &autofs4_root_inode_operations;
|
||||
root_inode->i_fop = &autofs4_root_operations;
|
||||
root = d_alloc_root(root_inode);
|
||||
pipe = NULL;
|
||||
|
||||
if (!root)
|
||||
goto fail_iput;
|
||||
|
||||
/* Can this call block? */
|
||||
if (parse_options(data, &pipefd,
|
||||
&root_inode->i_uid, &root_inode->i_gid,
|
||||
&sbi->oz_pgrp,
|
||||
&minproto, &maxproto)) {
|
||||
printk("autofs: called with bogus options\n");
|
||||
goto fail_dput;
|
||||
}
|
||||
|
||||
/* Couldn't this be tested earlier? */
|
||||
if (maxproto < AUTOFS_MIN_PROTO_VERSION ||
|
||||
minproto > AUTOFS_MAX_PROTO_VERSION) {
|
||||
printk("autofs: kernel does not match daemon version "
|
||||
"daemon (%d, %d) kernel (%d, %d)\n",
|
||||
minproto, maxproto,
|
||||
AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
|
||||
goto fail_dput;
|
||||
}
|
||||
|
||||
sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto;
|
||||
sbi->sub_version = AUTOFS_PROTO_SUBVERSION;
|
||||
|
||||
DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp);
|
||||
pipe = fget(pipefd);
|
||||
|
||||
if ( !pipe ) {
|
||||
printk("autofs: could not open pipe file descriptor\n");
|
||||
goto fail_dput;
|
||||
}
|
||||
if ( !pipe->f_op || !pipe->f_op->write )
|
||||
goto fail_fput;
|
||||
sbi->pipe = pipe;
|
||||
|
||||
/*
|
||||
* Success! Install the root dentry now to indicate completion.
|
||||
*/
|
||||
s->s_root = root;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Failure ... clean up.
|
||||
*/
|
||||
fail_fput:
|
||||
printk("autofs: pipe file descriptor does not contain proper ops\n");
|
||||
fput(pipe);
|
||||
/* fall through */
|
||||
fail_dput:
|
||||
dput(root);
|
||||
goto fail_free;
|
||||
fail_iput:
|
||||
printk("autofs: get root dentry failed\n");
|
||||
iput(root_inode);
|
||||
fail_free:
|
||||
kfree(sbi);
|
||||
fail_unlock:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct inode *autofs4_get_inode(struct super_block *sb,
|
||||
struct autofs_info *inf)
|
||||
{
|
||||
struct inode *inode = new_inode(sb);
|
||||
|
||||
if (inode == NULL)
|
||||
return NULL;
|
||||
|
||||
inf->inode = inode;
|
||||
inode->i_mode = inf->mode;
|
||||
if (sb->s_root) {
|
||||
inode->i_uid = sb->s_root->d_inode->i_uid;
|
||||
inode->i_gid = sb->s_root->d_inode->i_gid;
|
||||
} else {
|
||||
inode->i_uid = 0;
|
||||
inode->i_gid = 0;
|
||||
}
|
||||
inode->i_blksize = PAGE_CACHE_SIZE;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
|
||||
if (S_ISDIR(inf->mode)) {
|
||||
inode->i_nlink = 2;
|
||||
inode->i_op = &autofs4_dir_inode_operations;
|
||||
inode->i_fop = &autofs4_dir_operations;
|
||||
} else if (S_ISLNK(inf->mode)) {
|
||||
inode->i_size = inf->size;
|
||||
inode->i_op = &autofs4_symlink_inode_operations;
|
||||
}
|
||||
|
||||
return inode;
|
||||
}
|
808
fs/autofs4/root.c
Normal file
808
fs/autofs4/root.c
Normal file
@@ -0,0 +1,808 @@
|
||||
/* -*- c -*- --------------------------------------------------------------- *
|
||||
*
|
||||
* linux/fs/autofs/root.c
|
||||
*
|
||||
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
|
||||
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
|
||||
* Copyright 2001-2003 Ian Kent <raven@themaw.net>
|
||||
*
|
||||
* This file is part of the Linux kernel and is made available under
|
||||
* the terms of the GNU General Public License, version 2, or at your
|
||||
* option, any later version, incorporated herein by reference.
|
||||
*
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include "autofs_i.h"
|
||||
|
||||
static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *);
|
||||
static int autofs4_dir_unlink(struct inode *,struct dentry *);
|
||||
static int autofs4_dir_rmdir(struct inode *,struct dentry *);
|
||||
static int autofs4_dir_mkdir(struct inode *,struct dentry *,int);
|
||||
static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
|
||||
static int autofs4_dir_open(struct inode *inode, struct file *file);
|
||||
static int autofs4_dir_close(struct inode *inode, struct file *file);
|
||||
static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir);
|
||||
static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir);
|
||||
static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
|
||||
static int autofs4_dcache_readdir(struct file *, void *, filldir_t);
|
||||
|
||||
struct file_operations autofs4_root_operations = {
|
||||
.open = dcache_dir_open,
|
||||
.release = dcache_dir_close,
|
||||
.read = generic_read_dir,
|
||||
.readdir = autofs4_root_readdir,
|
||||
.ioctl = autofs4_root_ioctl,
|
||||
};
|
||||
|
||||
struct file_operations autofs4_dir_operations = {
|
||||
.open = autofs4_dir_open,
|
||||
.release = autofs4_dir_close,
|
||||
.read = generic_read_dir,
|
||||
.readdir = autofs4_dir_readdir,
|
||||
};
|
||||
|
||||
struct inode_operations autofs4_root_inode_operations = {
|
||||
.lookup = autofs4_lookup,
|
||||
.unlink = autofs4_dir_unlink,
|
||||
.symlink = autofs4_dir_symlink,
|
||||
.mkdir = autofs4_dir_mkdir,
|
||||
.rmdir = autofs4_dir_rmdir,
|
||||
};
|
||||
|
||||
struct inode_operations autofs4_dir_inode_operations = {
|
||||
.lookup = autofs4_lookup,
|
||||
.unlink = autofs4_dir_unlink,
|
||||
.symlink = autofs4_dir_symlink,
|
||||
.mkdir = autofs4_dir_mkdir,
|
||||
.rmdir = autofs4_dir_rmdir,
|
||||
};
|
||||
|
||||
static int autofs4_root_readdir(struct file *file, void *dirent,
|
||||
filldir_t filldir)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb);
|
||||
int oz_mode = autofs4_oz_mode(sbi);
|
||||
|
||||
DPRINTK("called, filp->f_pos = %lld", file->f_pos);
|
||||
|
||||
/*
|
||||
* Don't set reghost flag if:
|
||||
* 1) f_pos is larger than zero -- we've already been here.
|
||||
* 2) we haven't even enabled reghosting in the 1st place.
|
||||
* 3) this is the daemon doing a readdir
|
||||
*/
|
||||
if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled)
|
||||
sbi->needs_reghost = 1;
|
||||
|
||||
DPRINTK("needs_reghost = %d", sbi->needs_reghost);
|
||||
|
||||
return autofs4_dcache_readdir(file, dirent, filldir);
|
||||
}
|
||||
|
||||
/* Update usage from here to top of tree, so that scan of
|
||||
top-level directories will give a useful result */
|
||||
static void autofs4_update_usage(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *top = dentry->d_sb->s_root;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
for(; dentry != top; dentry = dentry->d_parent) {
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
|
||||
if (ino) {
|
||||
update_atime(dentry->d_inode);
|
||||
ino->last_used = jiffies;
|
||||
}
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* From 2.4 kernel readdir.c
|
||||
*/
|
||||
static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
|
||||
{
|
||||
int i;
|
||||
struct dentry *dentry = filp->f_dentry;
|
||||
|
||||
i = filp->f_pos;
|
||||
switch (i) {
|
||||
case 0:
|
||||
if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0)
|
||||
break;
|
||||
i++;
|
||||
filp->f_pos++;
|
||||
/* fallthrough */
|
||||
case 1:
|
||||
if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
|
||||
break;
|
||||
i++;
|
||||
filp->f_pos++;
|
||||
/* fallthrough */
|
||||
default: {
|
||||
struct list_head *list;
|
||||
int j = i-2;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
list = dentry->d_subdirs.next;
|
||||
|
||||
for (;;) {
|
||||
if (list == &dentry->d_subdirs) {
|
||||
spin_unlock(&dcache_lock);
|
||||
return 0;
|
||||
}
|
||||
if (!j)
|
||||
break;
|
||||
j--;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
struct dentry *de = list_entry(list, struct dentry, d_child);
|
||||
|
||||
if (!d_unhashed(de) && de->d_inode) {
|
||||
spin_unlock(&dcache_lock);
|
||||
if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0)
|
||||
break;
|
||||
spin_lock(&dcache_lock);
|
||||
}
|
||||
filp->f_pos++;
|
||||
list = list->next;
|
||||
if (list != &dentry->d_subdirs)
|
||||
continue;
|
||||
spin_unlock(&dcache_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autofs4_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct vfsmount *mnt = file->f_vfsmnt;
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
int status;
|
||||
|
||||
DPRINTK("file=%p dentry=%p %.*s",
|
||||
file, dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
if (autofs4_oz_mode(sbi))
|
||||
goto out;
|
||||
|
||||
if (autofs4_ispending(dentry)) {
|
||||
DPRINTK("dentry busy");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) {
|
||||
struct nameidata nd;
|
||||
int empty;
|
||||
|
||||
/* In case there are stale directory dentrys from a failed mount */
|
||||
spin_lock(&dcache_lock);
|
||||
empty = list_empty(&dentry->d_subdirs);
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
if (!empty)
|
||||
d_invalidate(dentry);
|
||||
|
||||
nd.flags = LOOKUP_DIRECTORY;
|
||||
status = (dentry->d_op->d_revalidate)(dentry, &nd);
|
||||
|
||||
if (!status)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (d_mountpoint(dentry)) {
|
||||
struct file *fp = NULL;
|
||||
struct vfsmount *fp_mnt = mntget(mnt);
|
||||
struct dentry *fp_dentry = dget(dentry);
|
||||
|
||||
while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry));
|
||||
|
||||
fp = dentry_open(fp_dentry, fp_mnt, file->f_flags);
|
||||
status = PTR_ERR(fp);
|
||||
if (IS_ERR(fp)) {
|
||||
file->private_data = NULL;
|
||||
return status;
|
||||
}
|
||||
file->private_data = fp;
|
||||
}
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autofs4_dir_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
|
||||
DPRINTK("file=%p dentry=%p %.*s",
|
||||
file, dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
if (autofs4_oz_mode(sbi))
|
||||
goto out;
|
||||
|
||||
if (autofs4_ispending(dentry)) {
|
||||
DPRINTK("dentry busy");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (d_mountpoint(dentry)) {
|
||||
struct file *fp = file->private_data;
|
||||
|
||||
if (!fp)
|
||||
return -ENOENT;
|
||||
|
||||
filp_close(fp, current->files);
|
||||
file->private_data = NULL;
|
||||
}
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
int status;
|
||||
|
||||
DPRINTK("file=%p dentry=%p %.*s",
|
||||
file, dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
if (autofs4_oz_mode(sbi))
|
||||
goto out;
|
||||
|
||||
if (autofs4_ispending(dentry)) {
|
||||
DPRINTK("dentry busy");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (d_mountpoint(dentry)) {
|
||||
struct file *fp = file->private_data;
|
||||
|
||||
if (!fp)
|
||||
return -ENOENT;
|
||||
|
||||
if (!fp->f_op || !fp->f_op->readdir)
|
||||
goto out;
|
||||
|
||||
status = vfs_readdir(fp, filldir, dirent);
|
||||
file->f_pos = fp->f_pos;
|
||||
if (status)
|
||||
autofs4_copy_atime(file, fp);
|
||||
return status;
|
||||
}
|
||||
out:
|
||||
return autofs4_dcache_readdir(file, dirent, filldir);
|
||||
}
|
||||
|
||||
static int try_to_fill_dentry(struct dentry *dentry,
|
||||
struct super_block *sb,
|
||||
struct autofs_sb_info *sbi, int flags)
|
||||
{
|
||||
struct autofs_info *de_info = autofs4_dentry_ino(dentry);
|
||||
int status = 0;
|
||||
|
||||
/* Block on any pending expiry here; invalidate the dentry
|
||||
when expiration is done to trigger mount request with a new
|
||||
dentry */
|
||||
if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) {
|
||||
DPRINTK("waiting for expire %p name=%.*s",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
status = autofs4_wait(sbi, dentry, NFY_NONE);
|
||||
|
||||
DPRINTK("expire done status=%d", status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DPRINTK("dentry=%p %.*s ino=%p",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
|
||||
|
||||
/* Wait for a pending mount, triggering one if there isn't one already */
|
||||
if (dentry->d_inode == NULL) {
|
||||
DPRINTK("waiting for mount name=%.*s",
|
||||
dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
|
||||
|
||||
DPRINTK("mount done status=%d", status);
|
||||
|
||||
if (status && dentry->d_inode)
|
||||
return 0; /* Try to get the kernel to invalidate this dentry */
|
||||
|
||||
/* Turn this into a real negative dentry? */
|
||||
if (status == -ENOENT) {
|
||||
dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT;
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 1;
|
||||
} else if (status) {
|
||||
/* Return a negative dentry, but leave it "pending" */
|
||||
return 1;
|
||||
}
|
||||
/* Trigger mount for path component or follow link */
|
||||
} else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ||
|
||||
current->link_count) {
|
||||
DPRINTK("waiting for mount name=%.*s",
|
||||
dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_AUTOFS_PENDING;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
|
||||
|
||||
DPRINTK("mount done status=%d", status);
|
||||
|
||||
if (status) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* We don't update the usages for the autofs daemon itself, this
|
||||
is necessary for recursive autofs mounts */
|
||||
if (!autofs4_oz_mode(sbi))
|
||||
autofs4_update_usage(dentry);
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Revalidate is called on every cache lookup. Some of those
|
||||
* cache lookups may actually happen while the dentry is not
|
||||
* yet completely filled in, and revalidate has to delay such
|
||||
* lookups..
|
||||
*/
|
||||
static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd)
|
||||
{
|
||||
struct inode * dir = dentry->d_parent->d_inode;
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
|
||||
int oz_mode = autofs4_oz_mode(sbi);
|
||||
int flags = nd ? nd->flags : 0;
|
||||
int status = 1;
|
||||
|
||||
/* Pending dentry */
|
||||
if (autofs4_ispending(dentry)) {
|
||||
if (!oz_mode)
|
||||
status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Negative dentry.. invalidate if "old" */
|
||||
if (dentry->d_inode == NULL)
|
||||
return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT);
|
||||
|
||||
/* Check for a non-mountpoint directory with no contents */
|
||||
spin_lock(&dcache_lock);
|
||||
if (S_ISDIR(dentry->d_inode->i_mode) &&
|
||||
!d_mountpoint(dentry) &&
|
||||
list_empty(&dentry->d_subdirs)) {
|
||||
DPRINTK("dentry=%p %.*s, emptydir",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
spin_unlock(&dcache_lock);
|
||||
if (!oz_mode)
|
||||
status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags);
|
||||
return status;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
/* Update the usage list */
|
||||
if (!oz_mode)
|
||||
autofs4_update_usage(dentry);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void autofs4_dentry_release(struct dentry *de)
|
||||
{
|
||||
struct autofs_info *inf;
|
||||
|
||||
DPRINTK("releasing %p", de);
|
||||
|
||||
inf = autofs4_dentry_ino(de);
|
||||
de->d_fsdata = NULL;
|
||||
|
||||
if (inf) {
|
||||
inf->dentry = NULL;
|
||||
inf->inode = NULL;
|
||||
|
||||
autofs4_free_ino(inf);
|
||||
}
|
||||
}
|
||||
|
||||
/* For dentries of directories in the root dir */
|
||||
static struct dentry_operations autofs4_root_dentry_operations = {
|
||||
.d_revalidate = autofs4_revalidate,
|
||||
.d_release = autofs4_dentry_release,
|
||||
};
|
||||
|
||||
/* For other dentries */
|
||||
static struct dentry_operations autofs4_dentry_operations = {
|
||||
.d_revalidate = autofs4_revalidate,
|
||||
.d_release = autofs4_dentry_release,
|
||||
};
|
||||
|
||||
/* Lookups in the root directory */
|
||||
static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct autofs_sb_info *sbi;
|
||||
int oz_mode;
|
||||
|
||||
DPRINTK("name = %.*s",
|
||||
dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
if (dentry->d_name.len > NAME_MAX)
|
||||
return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */
|
||||
|
||||
sbi = autofs4_sbi(dir->i_sb);
|
||||
|
||||
oz_mode = autofs4_oz_mode(sbi);
|
||||
DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
|
||||
current->pid, process_group(current), 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 = &autofs4_root_dentry_operations;
|
||||
|
||||
if (!oz_mode) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_AUTOFS_PENDING;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
dentry->d_fsdata = NULL;
|
||||
d_add(dentry, NULL);
|
||||
|
||||
if (dentry->d_op && dentry->d_op->d_revalidate) {
|
||||
up(&dir->i_sem);
|
||||
(dentry->d_op->d_revalidate)(dentry, nd);
|
||||
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) {
|
||||
/* See if we were interrupted */
|
||||
if (signal_pending(current)) {
|
||||
sigset_t *sigset = ¤t->pending.signal;
|
||||
if (sigismember (sigset, SIGKILL) ||
|
||||
sigismember (sigset, SIGQUIT) ||
|
||||
sigismember (sigset, SIGINT)) {
|
||||
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;
|
||||
}
|
||||
|
||||
static int autofs4_dir_symlink(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
const char *symname)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
struct inode *inode;
|
||||
char *cp;
|
||||
|
||||
DPRINTK("%s <- %.*s", symname,
|
||||
dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
if (!autofs4_oz_mode(sbi))
|
||||
return -EACCES;
|
||||
|
||||
ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555);
|
||||
if (ino == NULL)
|
||||
return -ENOSPC;
|
||||
|
||||
ino->size = strlen(symname);
|
||||
ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL);
|
||||
|
||||
if (cp == NULL) {
|
||||
kfree(ino);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
strcpy(cp, symname);
|
||||
|
||||
inode = autofs4_get_inode(dir->i_sb, ino);
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
if (dir == dir->i_sb->s_root->d_inode)
|
||||
dentry->d_op = &autofs4_root_dentry_operations;
|
||||
else
|
||||
dentry->d_op = &autofs4_dentry_operations;
|
||||
|
||||
dentry->d_fsdata = ino;
|
||||
ino->dentry = dget(dentry);
|
||||
ino->inode = inode;
|
||||
|
||||
dir->i_mtime = CURRENT_TIME;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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. We don't want
|
||||
* this, because since the unlink is probably the result of an expire.
|
||||
* We simply d_drop it, which allows the dentry lookup to remount it
|
||||
* if necessary.
|
||||
*
|
||||
* If a process is blocked on the dentry waiting for the expire to finish,
|
||||
* it will invalidate the dentry and try to mount with a new one.
|
||||
*
|
||||
* Also see autofs4_dir_rmdir()..
|
||||
*/
|
||||
static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
|
||||
/* This allows root to remove symlinks */
|
||||
if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
|
||||
return -EACCES;
|
||||
|
||||
dput(ino->dentry);
|
||||
|
||||
dentry->d_inode->i_size = 0;
|
||||
dentry->d_inode->i_nlink = 0;
|
||||
|
||||
dir->i_mtime = CURRENT_TIME;
|
||||
|
||||
d_drop(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
|
||||
if (!autofs4_oz_mode(sbi))
|
||||
return -EACCES;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
if (!list_empty(&dentry->d_subdirs)) {
|
||||
spin_unlock(&dcache_lock);
|
||||
return -ENOTEMPTY;
|
||||
}
|
||||
spin_lock(&dentry->d_lock);
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
dput(ino->dentry);
|
||||
|
||||
dentry->d_inode->i_size = 0;
|
||||
dentry->d_inode->i_nlink = 0;
|
||||
|
||||
if (dir->i_nlink)
|
||||
dir->i_nlink--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
struct inode *inode;
|
||||
|
||||
if ( !autofs4_oz_mode(sbi) )
|
||||
return -EACCES;
|
||||
|
||||
DPRINTK("dentry %p, creating %.*s",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555);
|
||||
if (ino == NULL)
|
||||
return -ENOSPC;
|
||||
|
||||
inode = autofs4_get_inode(dir->i_sb, ino);
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
if (dir == dir->i_sb->s_root->d_inode)
|
||||
dentry->d_op = &autofs4_root_dentry_operations;
|
||||
else
|
||||
dentry->d_op = &autofs4_dentry_operations;
|
||||
|
||||
dentry->d_fsdata = ino;
|
||||
ino->dentry = dget(dentry);
|
||||
ino->inode = inode;
|
||||
dir->i_nlink++;
|
||||
dir->i_mtime = CURRENT_TIME;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get/set timeout ioctl() operation */
|
||||
static inline int autofs4_get_set_timeout(struct autofs_sb_info *sbi,
|
||||
unsigned long __user *p)
|
||||
{
|
||||
int rv;
|
||||
unsigned long ntimeout;
|
||||
|
||||
if ( (rv = get_user(ntimeout, p)) ||
|
||||
(rv = put_user(sbi->exp_timeout/HZ, p)) )
|
||||
return rv;
|
||||
|
||||
if ( ntimeout > ULONG_MAX/HZ )
|
||||
sbi->exp_timeout = 0;
|
||||
else
|
||||
sbi->exp_timeout = ntimeout * HZ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return protocol version */
|
||||
static inline int autofs4_get_protover(struct autofs_sb_info *sbi, int __user *p)
|
||||
{
|
||||
return put_user(sbi->version, p);
|
||||
}
|
||||
|
||||
/* Return protocol sub version */
|
||||
static inline int autofs4_get_protosubver(struct autofs_sb_info *sbi, int __user *p)
|
||||
{
|
||||
return put_user(sbi->sub_version, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tells the daemon whether we need to reghost or not. Also, clears
|
||||
* the reghost_needed flag.
|
||||
*/
|
||||
static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p)
|
||||
{
|
||||
int status;
|
||||
|
||||
DPRINTK("returning %d", sbi->needs_reghost);
|
||||
|
||||
status = put_user(sbi->needs_reghost, p);
|
||||
if ( status )
|
||||
return status;
|
||||
|
||||
sbi->needs_reghost = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable / Disable reghosting ioctl() operation
|
||||
*/
|
||||
static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p)
|
||||
{
|
||||
int status;
|
||||
int val;
|
||||
|
||||
status = get_user(val, p);
|
||||
|
||||
DPRINTK("reghost = %d", val);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* turn on/off reghosting, with the val */
|
||||
sbi->reghost_enabled = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tells the daemon whether it can umount the autofs mount.
|
||||
*/
|
||||
static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (may_umount(mnt) == 0)
|
||||
status = 1;
|
||||
|
||||
DPRINTK("returning %d", status);
|
||||
|
||||
status = put_user(status, p);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Identify autofs4_dentries - this is so we can tell if there's
|
||||
an extra dentry refcount or not. We only hold a refcount on the
|
||||
dentry if its non-negative (ie, d_inode != NULL)
|
||||
*/
|
||||
int is_autofs4_dentry(struct dentry *dentry)
|
||||
{
|
||||
return dentry && dentry->d_inode &&
|
||||
(dentry->d_op == &autofs4_root_dentry_operations ||
|
||||
dentry->d_op == &autofs4_dentry_operations) &&
|
||||
dentry->d_fsdata != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* ioctl()'s on the root directory is the chief method for the daemon to
|
||||
* generate kernel reactions
|
||||
*/
|
||||
static int autofs4_root_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(inode->i_sb);
|
||||
void __user *p = (void __user *)arg;
|
||||
|
||||
DPRINTK("cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u",
|
||||
cmd,arg,sbi,process_group(current));
|
||||
|
||||
if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
|
||||
_IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT )
|
||||
return -ENOTTY;
|
||||
|
||||
if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
|
||||
return -EPERM;
|
||||
|
||||
switch(cmd) {
|
||||
case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */
|
||||
return autofs4_wait_release(sbi,(autofs_wqt_t)arg,0);
|
||||
case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */
|
||||
return autofs4_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT);
|
||||
case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
|
||||
autofs4_catatonic_mode(sbi);
|
||||
return 0;
|
||||
case AUTOFS_IOC_PROTOVER: /* Get protocol version */
|
||||
return autofs4_get_protover(sbi, p);
|
||||
case AUTOFS_IOC_PROTOSUBVER: /* Get protocol sub version */
|
||||
return autofs4_get_protosubver(sbi, p);
|
||||
case AUTOFS_IOC_SETTIMEOUT:
|
||||
return autofs4_get_set_timeout(sbi, p);
|
||||
|
||||
case AUTOFS_IOC_TOGGLEREGHOST:
|
||||
return autofs4_toggle_reghost(sbi, p);
|
||||
case AUTOFS_IOC_ASKREGHOST:
|
||||
return autofs4_ask_reghost(sbi, p);
|
||||
|
||||
case AUTOFS_IOC_ASKUMOUNT:
|
||||
return autofs4_ask_umount(filp->f_vfsmnt, p);
|
||||
|
||||
/* return a single thing to expire */
|
||||
case AUTOFS_IOC_EXPIRE:
|
||||
return autofs4_expire_run(inode->i_sb,filp->f_vfsmnt,sbi, p);
|
||||
/* same as above, but can send multiple expires through pipe */
|
||||
case AUTOFS_IOC_EXPIRE_MULTI:
|
||||
return autofs4_expire_multi(inode->i_sb,filp->f_vfsmnt,sbi, p);
|
||||
|
||||
default:
|
||||
return -ENOSYS;
|
||||
}
|
||||
}
|
25
fs/autofs4/symlink.c
Normal file
25
fs/autofs4/symlink.c
Normal file
@@ -0,0 +1,25 @@
|
||||
/* -*- c -*- --------------------------------------------------------------- *
|
||||
*
|
||||
* linux/fs/autofs/symlink.c
|
||||
*
|
||||
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
|
||||
*
|
||||
* This file is part of the Linux kernel and is made available under
|
||||
* the terms of the GNU General Public License, version 2, or at your
|
||||
* option, any later version, incorporated herein by reference.
|
||||
*
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#include "autofs_i.h"
|
||||
|
||||
static int autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
nd_set_link(nd, (char *)ino->u.symlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct inode_operations autofs4_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = autofs4_follow_link
|
||||
};
|
303
fs/autofs4/waitq.c
Normal file
303
fs/autofs4/waitq.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/* -*- c -*- --------------------------------------------------------------- *
|
||||
*
|
||||
* linux/fs/autofs/waitq.c
|
||||
*
|
||||
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
|
||||
* Copyright 2001-2003 Ian Kent <raven@themaw.net>
|
||||
*
|
||||
* This file is part of the Linux kernel and is made available under
|
||||
* the terms of the GNU General Public License, version 2, or at your
|
||||
* option, any later version, incorporated herein by reference.
|
||||
*
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/file.h>
|
||||
#include "autofs_i.h"
|
||||
|
||||
/* We make this a static variable rather than a part of the superblock; it
|
||||
is better if we don't reassign numbers easily even across filesystems */
|
||||
static autofs_wqt_t autofs4_next_wait_queue = 1;
|
||||
|
||||
/* These are the signals we allow interrupting a pending mount */
|
||||
#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT))
|
||||
|
||||
void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
|
||||
{
|
||||
struct autofs_wait_queue *wq, *nwq;
|
||||
|
||||
DPRINTK("entering catatonic mode");
|
||||
|
||||
sbi->catatonic = 1;
|
||||
wq = sbi->queues;
|
||||
sbi->queues = NULL; /* Erase all wait queues */
|
||||
while ( wq ) {
|
||||
nwq = wq->next;
|
||||
wq->status = -ENOENT; /* Magic is gone - report failure */
|
||||
kfree(wq->name);
|
||||
wq->name = NULL;
|
||||
wake_up_interruptible(&wq->queue);
|
||||
wq = nwq;
|
||||
}
|
||||
if (sbi->pipe) {
|
||||
fput(sbi->pipe); /* Close the pipe */
|
||||
sbi->pipe = NULL;
|
||||
}
|
||||
|
||||
shrink_dcache_sb(sbi->sb);
|
||||
}
|
||||
|
||||
static int autofs4_write(struct file *file, const void *addr, int bytes)
|
||||
{
|
||||
unsigned long sigpipe, flags;
|
||||
mm_segment_t fs;
|
||||
const char *data = (const char *)addr;
|
||||
ssize_t wr = 0;
|
||||
|
||||
/** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
|
||||
|
||||
sigpipe = sigismember(¤t->pending.signal, SIGPIPE);
|
||||
|
||||
/* Save pointer to user space and point back to kernel space */
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
while (bytes &&
|
||||
(wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) {
|
||||
data += wr;
|
||||
bytes -= wr;
|
||||
}
|
||||
|
||||
set_fs(fs);
|
||||
|
||||
/* Keep the currently executing process from receiving a
|
||||
SIGPIPE unless it was already supposed to get one */
|
||||
if (wr == -EPIPE && !sigpipe) {
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
sigdelset(¤t->pending.signal, SIGPIPE);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
}
|
||||
|
||||
return (bytes > 0);
|
||||
}
|
||||
|
||||
static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
|
||||
struct autofs_wait_queue *wq,
|
||||
int type)
|
||||
{
|
||||
union autofs_packet_union pkt;
|
||||
size_t pktsz;
|
||||
|
||||
DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d",
|
||||
wq->wait_queue_token, wq->len, wq->name, type);
|
||||
|
||||
memset(&pkt,0,sizeof pkt); /* For security reasons */
|
||||
|
||||
pkt.hdr.proto_version = sbi->version;
|
||||
pkt.hdr.type = type;
|
||||
if (type == autofs_ptype_missing) {
|
||||
struct autofs_packet_missing *mp = &pkt.missing;
|
||||
|
||||
pktsz = sizeof(*mp);
|
||||
|
||||
mp->wait_queue_token = wq->wait_queue_token;
|
||||
mp->len = wq->len;
|
||||
memcpy(mp->name, wq->name, wq->len);
|
||||
mp->name[wq->len] = '\0';
|
||||
} else if (type == autofs_ptype_expire_multi) {
|
||||
struct autofs_packet_expire_multi *ep = &pkt.expire_multi;
|
||||
|
||||
pktsz = sizeof(*ep);
|
||||
|
||||
ep->wait_queue_token = wq->wait_queue_token;
|
||||
ep->len = wq->len;
|
||||
memcpy(ep->name, wq->name, wq->len);
|
||||
ep->name[wq->len] = '\0';
|
||||
} else {
|
||||
printk("autofs4_notify_daemon: bad type %d!\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (autofs4_write(sbi->pipe, &pkt, pktsz))
|
||||
autofs4_catatonic_mode(sbi);
|
||||
}
|
||||
|
||||
static int autofs4_getpath(struct autofs_sb_info *sbi,
|
||||
struct dentry *dentry, char **name)
|
||||
{
|
||||
struct dentry *root = sbi->sb->s_root;
|
||||
struct dentry *tmp;
|
||||
char *buf = *name;
|
||||
char *p;
|
||||
int len = 0;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent)
|
||||
len += tmp->d_name.len + 1;
|
||||
|
||||
if (--len > NAME_MAX) {
|
||||
spin_unlock(&dcache_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*(buf + len) = '\0';
|
||||
p = buf + len - dentry->d_name.len;
|
||||
strncpy(p, dentry->d_name.name, dentry->d_name.len);
|
||||
|
||||
for (tmp = dentry->d_parent; tmp != root ; tmp = tmp->d_parent) {
|
||||
*(--p) = '/';
|
||||
p -= tmp->d_name.len;
|
||||
strncpy(p, tmp->d_name.name, tmp->d_name.len);
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
|
||||
enum autofs_notify notify)
|
||||
{
|
||||
struct autofs_wait_queue *wq;
|
||||
char *name;
|
||||
int len, status;
|
||||
|
||||
/* In catatonic mode, we don't wait for nobody */
|
||||
if ( sbi->catatonic )
|
||||
return -ENOENT;
|
||||
|
||||
name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
len = autofs4_getpath(sbi, dentry, &name);
|
||||
if (!len) {
|
||||
kfree(name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (down_interruptible(&sbi->wq_sem)) {
|
||||
kfree(name);
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
for (wq = sbi->queues ; wq ; wq = wq->next) {
|
||||
if (wq->hash == dentry->d_name.hash &&
|
||||
wq->len == len &&
|
||||
wq->name && !memcmp(wq->name, name, len))
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !wq ) {
|
||||
/* Create a new wait queue */
|
||||
wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
|
||||
if ( !wq ) {
|
||||
kfree(name);
|
||||
up(&sbi->wq_sem);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wq->wait_queue_token = autofs4_next_wait_queue;
|
||||
if (++autofs4_next_wait_queue == 0)
|
||||
autofs4_next_wait_queue = 1;
|
||||
wq->next = sbi->queues;
|
||||
sbi->queues = wq;
|
||||
init_waitqueue_head(&wq->queue);
|
||||
wq->hash = dentry->d_name.hash;
|
||||
wq->name = name;
|
||||
wq->len = len;
|
||||
wq->status = -EINTR; /* Status return if interrupted */
|
||||
atomic_set(&wq->wait_ctr, 2);
|
||||
up(&sbi->wq_sem);
|
||||
|
||||
DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d",
|
||||
(unsigned long) wq->wait_queue_token, wq->len, wq->name, notify);
|
||||
/* autofs4_notify_daemon() may block */
|
||||
if (notify != NFY_NONE) {
|
||||
autofs4_notify_daemon(sbi,wq,
|
||||
notify == NFY_MOUNT ?
|
||||
autofs_ptype_missing :
|
||||
autofs_ptype_expire_multi);
|
||||
}
|
||||
} else {
|
||||
atomic_inc(&wq->wait_ctr);
|
||||
up(&sbi->wq_sem);
|
||||
kfree(name);
|
||||
DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d",
|
||||
(unsigned long) wq->wait_queue_token, wq->len, wq->name, notify);
|
||||
}
|
||||
|
||||
/* wq->name is NULL if and only if the lock is already released */
|
||||
|
||||
if ( sbi->catatonic ) {
|
||||
/* We might have slept, so check again for catatonic mode */
|
||||
wq->status = -ENOENT;
|
||||
if ( wq->name ) {
|
||||
kfree(wq->name);
|
||||
wq->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ( wq->name ) {
|
||||
/* Block all but "shutdown" signals while waiting */
|
||||
sigset_t oldset;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(¤t->sighand->siglock, irqflags);
|
||||
oldset = current->blocked;
|
||||
siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, irqflags);
|
||||
|
||||
wait_event_interruptible(wq->queue, wq->name == NULL);
|
||||
|
||||
spin_lock_irqsave(¤t->sighand->siglock, irqflags);
|
||||
current->blocked = oldset;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, irqflags);
|
||||
} else {
|
||||
DPRINTK("skipped sleeping");
|
||||
}
|
||||
|
||||
status = wq->status;
|
||||
|
||||
/* Are we the last process to need status? */
|
||||
if (atomic_dec_and_test(&wq->wait_ctr))
|
||||
kfree(wq);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status)
|
||||
{
|
||||
struct autofs_wait_queue *wq, **wql;
|
||||
|
||||
down(&sbi->wq_sem);
|
||||
for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) {
|
||||
if ( wq->wait_queue_token == wait_queue_token )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !wq ) {
|
||||
up(&sbi->wq_sem);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*wql = wq->next; /* Unlink from chain */
|
||||
up(&sbi->wq_sem);
|
||||
kfree(wq->name);
|
||||
wq->name = NULL; /* Do not wait on this queue */
|
||||
|
||||
wq->status = status;
|
||||
|
||||
if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */
|
||||
kfree(wq);
|
||||
else
|
||||
wake_up_interruptible(&wq->queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user