2005-04-17 02:20:36 +04:00
/* -*- linux-c -*- --------------------------------------------------------- *
*
* linux / fs / autofs / root . 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 .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# 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 autofs_root_readdir ( struct file * , void * , filldir_t ) ;
static struct dentry * autofs_root_lookup ( struct inode * , struct dentry * , struct nameidata * ) ;
static int autofs_root_symlink ( struct inode * , struct dentry * , const char * ) ;
static int autofs_root_unlink ( struct inode * , struct dentry * ) ;
static int autofs_root_rmdir ( struct inode * , struct dentry * ) ;
static int autofs_root_mkdir ( struct inode * , struct dentry * , int ) ;
static int autofs_root_ioctl ( struct inode * , struct file * , unsigned int , unsigned long ) ;
2006-03-28 13:56:42 +04:00
const struct file_operations autofs_root_operations = {
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
. readdir = autofs_root_readdir ,
. ioctl = autofs_root_ioctl ,
} ;
2007-02-12 11:55:38 +03:00
const struct inode_operations autofs_root_inode_operations = {
2005-04-17 02:20:36 +04:00
. lookup = autofs_root_lookup ,
. unlink = autofs_root_unlink ,
. symlink = autofs_root_symlink ,
. mkdir = autofs_root_mkdir ,
. rmdir = autofs_root_rmdir ,
} ;
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 ;
2006-12-08 13:36:46 +03:00
struct inode * inode = filp - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
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 :
2007-05-11 09:23:06 +04:00
while ( onr = nr , ent = autofs_hash_enum ( dirhash , & nr , ent ) ) {
if ( ! ent - > dentry | | d_mountpoint ( ent - > dentry ) ) {
2005-04-17 02:20:36 +04:00
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 ;
}
static int try_to_fill_dentry ( struct dentry * dentry , struct super_block * sb , struct autofs_sb_info * sbi )
{
struct inode * inode ;
struct autofs_dir_ent * ent ;
int status = 0 ;
2007-05-11 09:23:06 +04:00
if ( ! ( ent = autofs_hash_lookup ( & sbi - > dirhash , & dentry - > d_name ) ) ) {
2005-04-17 02:20:36 +04:00
do {
2007-05-11 09:23:06 +04:00
if ( status & & dentry - > d_inode ) {
if ( status ! = - ENOENT )
2005-04-17 02:20:36 +04:00
printk ( " autofs warning: lookup failure on positive dentry, status = %d, name = %s \n " , status , dentry - > d_name . name ) ;
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 ;
dentry - > d_flags & = ~ DCACHE_AUTOFS_PENDING ;
return 1 ;
} else if ( status ) {
/* Return a negative dentry, but leave it "pending" */
return 1 ;
}
status = autofs_wait ( sbi , & dentry - > d_name ) ;
2007-05-11 09:23:06 +04:00
} while ( ! ( ent = autofs_hash_lookup ( & sbi - > dirhash , & dentry - > d_name ) ) ) ;
2005-04-17 02:20:36 +04:00
}
/* Abuse this field as a pointer to the directory entry, used to
find the expire list pointers */
dentry - > d_time = ( unsigned long ) ent ;
if ( ! dentry - > d_inode ) {
inode = iget ( sb , ent - > ino ) ;
if ( ! inode ) {
/* Failed, but leave pending for next time */
return 1 ;
}
dentry - > d_inode = inode ;
}
/* If this is a directory that isn't a mount point, bitch at the
daemon and fix it in user space */
2007-05-11 09:23:06 +04:00
if ( S_ISDIR ( dentry - > d_inode - > i_mode ) & & ! d_mountpoint ( dentry ) ) {
2005-04-17 02:20:36 +04:00
return ! autofs_wait ( sbi , & dentry - > d_name ) ;
}
/* We don't update the usages for the autofs daemon itself, this
is necessary for recursive autofs mounts */
2007-05-11 09:23:06 +04:00
if ( ! autofs_oz_mode ( sbi ) ) {
2005-04-17 02:20:36 +04:00
autofs_update_usage ( & sbi - > dirhash , ent ) ;
}
dentry - > d_flags & = ~ DCACHE_AUTOFS_PENDING ;
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 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 */
2007-05-11 09:23:06 +04:00
if ( dentry - > d_flags & DCACHE_AUTOFS_PENDING ) {
2005-04-17 02:20:36 +04:00
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 */
2007-05-11 09:23:06 +04:00
if ( S_ISDIR ( dentry - > d_inode - > i_mode ) & & ! d_mountpoint ( dentry ) ) {
2005-04-17 02:20:36 +04:00
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 */
2007-05-11 09:23:06 +04:00
if ( ! autofs_oz_mode ( sbi ) ) {
2005-04-17 02:20:36 +04:00
ent = ( struct autofs_dir_ent * ) dentry - > d_time ;
2007-05-11 09:23:06 +04:00
if ( ent )
2005-04-17 02:20:36 +04:00
autofs_update_usage ( & sbi - > dirhash , ent ) ;
}
unlock_kernel ( ) ;
return 1 ;
}
static struct dentry_operations autofs_dentry_operations = {
. d_revalidate = autofs_revalidate ,
} ;
static struct dentry * autofs_root_lookup ( struct inode * dir , struct dentry * dentry , struct nameidata * nd )
{
struct autofs_sb_info * sbi ;
int oz_mode ;
DPRINTK ( ( " autofs_root_lookup: name = " ) ) ;
lock_kernel ( ) ;
autofs_say ( dentry - > d_name . name , dentry - > d_name . len ) ;
if ( dentry - > d_name . len > NAME_MAX ) {
unlock_kernel ( ) ;
return ERR_PTR ( - ENAMETOOLONG ) ; /* File name too long to exist */
}
sbi = autofs_sbi ( dir - > i_sb ) ;
oz_mode = autofs_oz_mode ( sbi ) ;
2007-05-11 09:23:08 +04:00
DPRINTK ( ( " autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, "
" oz_mode = %d \n " , pid_nr ( task_pid ( current ) ) ,
process_group ( current ) , sbi - > catatonic ,
oz_mode ) ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & dir - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
autofs_revalidate ( dentry , nd ) ;
2006-01-10 02:59:24 +03:00
mutex_lock ( & dir - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 = & current - > pending . signal ;
if ( sigismember ( sigset , SIGKILL ) | |
sigismember ( sigset , SIGQUIT ) | |
sigismember ( sigset , SIGINT ) ) {
unlock_kernel ( ) ;
return ERR_PTR ( - ERESTARTNOINTR ) ;
}
}
}
unlock_kernel ( ) ;
/*
* 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 .
*/
2007-05-11 09:23:06 +04:00
if ( dentry - > d_inode & & d_unhashed ( dentry ) )
2005-04-17 02:20:36 +04:00
return ERR_PTR ( - ENOENT ) ;
return NULL ;
}
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 ) ;
lock_kernel ( ) ;
2007-05-11 09:23:06 +04:00
if ( ! autofs_oz_mode ( sbi ) ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - EACCES ;
}
2007-05-11 09:23:06 +04:00
if ( autofs_hash_lookup ( dh , & dentry - > d_name ) ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - EEXIST ;
}
n = find_first_zero_bit ( sbi - > symlink_bitmap , AUTOFS_MAX_SYMLINKS ) ;
2007-05-11 09:23:06 +04:00
if ( n > = AUTOFS_MAX_SYMLINKS ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
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 ) ;
2007-05-11 09:23:06 +04:00
if ( ! sl - > data ) {
2005-04-17 02:20:36 +04:00
clear_bit ( n , sbi - > symlink_bitmap ) ;
unlock_kernel ( ) ;
return - ENOSPC ;
}
ent = kmalloc ( sizeof ( struct autofs_dir_ent ) , GFP_KERNEL ) ;
2007-05-11 09:23:06 +04:00
if ( ! ent ) {
2005-04-17 02:20:36 +04:00
kfree ( sl - > data ) ;
clear_bit ( n , sbi - > symlink_bitmap ) ;
unlock_kernel ( ) ;
return - ENOSPC ;
}
ent - > name = kmalloc ( dentry - > d_name . len + 1 , GFP_KERNEL ) ;
2007-05-11 09:23:06 +04:00
if ( ! ent - > name ) {
2005-04-17 02:20:36 +04:00
kfree ( sl - > data ) ;
kfree ( ent ) ;
clear_bit ( n , sbi - > symlink_bitmap ) ;
unlock_kernel ( ) ;
return - ENOSPC ;
}
memcpy ( sl - > data , symname , slsize ) ;
sl - > mtime = get_seconds ( ) ;
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 ) ) ;
unlock_kernel ( ) ;
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 , 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 */
lock_kernel ( ) ;
2007-05-11 09:23:06 +04:00
if ( ! autofs_oz_mode ( sbi ) & & ! capable ( CAP_SYS_ADMIN ) ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - EACCES ;
}
ent = autofs_hash_lookup ( dh , & dentry - > d_name ) ;
2007-05-11 09:23:06 +04:00
if ( ! ent ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - ENOENT ;
}
n = ent - > ino - AUTOFS_FIRST_SYMLINK ;
2007-05-11 09:23:06 +04:00
if ( n > = AUTOFS_MAX_SYMLINKS ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - EISDIR ; /* It's a directory, dummy */
}
2007-05-11 09:23:06 +04:00
if ( ! test_bit ( n , sbi - > symlink_bitmap ) ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
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 ) ;
unlock_kernel ( ) ;
return 0 ;
}
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 ( ) ;
2007-05-11 09:23:06 +04:00
if ( ! autofs_oz_mode ( sbi ) ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - EACCES ;
}
ent = autofs_hash_lookup ( dh , & dentry - > d_name ) ;
2007-05-11 09:23:06 +04:00
if ( ! ent ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - ENOENT ;
}
2007-05-11 09:23:06 +04:00
if ( ( unsigned int ) ent - > ino < AUTOFS_FIRST_DIR_INO ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - ENOTDIR ; /* Not a directory */
}
2007-05-11 09:23:06 +04:00
if ( ent - > dentry ! = dentry ) {
2005-04-17 02:20:36 +04:00
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 ) ;
2006-10-01 10:29:03 +04:00
drop_nlink ( dir ) ;
2005-04-17 02:20:36 +04:00
d_drop ( dentry ) ;
unlock_kernel ( ) ;
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 ;
ino_t ino ;
lock_kernel ( ) ;
2007-05-11 09:23:06 +04:00
if ( ! autofs_oz_mode ( sbi ) ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - EACCES ;
}
ent = autofs_hash_lookup ( dh , & dentry - > d_name ) ;
2007-05-11 09:23:06 +04:00
if ( ent ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - EEXIST ;
}
2007-05-11 09:23:06 +04:00
if ( sbi - > next_dir_ino < AUTOFS_FIRST_DIR_INO ) {
2005-04-17 02:20:36 +04:00
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 ) ;
2007-05-11 09:23:06 +04:00
if ( ! ent ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - ENOSPC ;
}
ent - > name = kmalloc ( dentry - > d_name . len + 1 , GFP_KERNEL ) ;
2007-05-11 09:23:06 +04:00
if ( ! ent - > name ) {
2005-04-17 02:20:36 +04:00
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 ) ;
2006-10-01 10:29:04 +04:00
inc_nlink ( dir ) ;
2005-04-17 02:20:36 +04:00
d_instantiate ( dentry , iget ( dir - > i_sb , ino ) ) ;
unlock_kernel ( ) ;
return 0 ;
}
/* Get/set timeout ioctl() operation */
static inline int autofs_get_set_timeout ( struct autofs_sb_info * sbi ,
unsigned long __user * p )
{
unsigned long ntimeout ;
if ( get_user ( ntimeout , p ) | |
put_user ( sbi - > exp_timeout / HZ , p ) )
return - EFAULT ;
2007-05-11 09:23:06 +04:00
if ( ntimeout > ULONG_MAX / HZ )
2005-04-17 02:20:36 +04:00
sbi - > exp_timeout = 0 ;
else
sbi - > exp_timeout = ntimeout * HZ ;
return 0 ;
}
/* Return protocol version */
static inline int autofs_get_protover ( int __user * p )
{
return put_user ( AUTOFS_PROTO_VERSION , p ) ;
}
/* Perform an expiry operation */
static inline int autofs_expire_run ( struct super_block * sb ,
struct autofs_sb_info * sbi ,
struct vfsmount * mnt ,
struct autofs_packet_expire __user * pkt_p )
{
struct autofs_dir_ent * ent ;
struct autofs_packet_expire pkt ;
memset ( & pkt , 0 , sizeof pkt ) ;
pkt . hdr . proto_version = AUTOFS_PROTO_VERSION ;
pkt . hdr . type = autofs_ptype_expire ;
2007-05-11 09:23:06 +04:00
if ( ! sbi - > exp_timeout | | ! ( ent = autofs_expire ( sb , sbi , mnt ) ) )
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
pkt . len = ent - > len ;
memcpy ( pkt . name , ent - > name , pkt . len ) ;
pkt . name [ pkt . len ] = ' \0 ' ;
2007-05-11 09:23:06 +04:00
if ( copy_to_user ( pkt_p , & pkt , sizeof ( struct autofs_packet_expire ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
}
/*
* ioctl ( ) ' s on the root directory is the chief method for the daemon to
* generate kernel reactions
*/
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 , process_group ( current ) ) ) ;
2007-05-11 09:23:06 +04:00
if ( _IOC_TYPE ( cmd ) ! = _IOC_TYPE ( AUTOFS_IOC_FIRST ) | |
_IOC_NR ( cmd ) - _IOC_NR ( AUTOFS_IOC_FIRST ) > = AUTOFS_IOC_COUNT )
2005-04-17 02:20:36 +04:00
return - ENOTTY ;
2007-05-11 09:23:06 +04:00
if ( ! autofs_oz_mode ( sbi ) & & ! capable ( CAP_SYS_ADMIN ) )
2005-04-17 02:20:36 +04:00
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 :
2006-12-08 13:36:46 +03:00
return autofs_expire_run ( inode - > i_sb , sbi , filp - > f_path . mnt ,
2005-04-17 02:20:36 +04:00
argp ) ;
default :
return - ENOSYS ;
}
}