2005-04-17 02:20:36 +04:00
/*
* fs / cifs / link . c
*
2008-01-25 13:12:41 +03:00
* Copyright ( C ) International Business Machines Corp . , 2002 , 2008
2005-04-17 02:20:36 +04:00
* Author ( s ) : Steve French ( sfrench @ us . ibm . com )
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/fs.h>
# include <linux/stat.h>
# include <linux/namei.h>
# include "cifsfs.h"
# include "cifspdu.h"
# include "cifsglob.h"
# include "cifsproto.h"
# include "cifs_debug.h"
# include "cifs_fs_sb.h"
int
cifs_hardlink ( struct dentry * old_file , struct inode * inode ,
struct dentry * direntry )
{
int rc = - EACCES ;
int xid ;
char * fromName = NULL ;
char * toName = NULL ;
struct cifs_sb_info * cifs_sb_target ;
struct cifsTconInfo * pTcon ;
struct cifsInodeInfo * cifsInode ;
xid = GetXid ( ) ;
cifs_sb_target = CIFS_SB ( inode - > i_sb ) ;
pTcon = cifs_sb_target - > tcon ;
/* No need to check for cross device links since server will do that
BB note DFS case in future though ( when we may have to check ) */
2005-08-30 22:32:14 +04:00
fromName = build_path_from_dentry ( old_file ) ;
toName = build_path_from_dentry ( direntry ) ;
2007-07-10 05:16:18 +04:00
if ( ( fromName = = NULL ) | | ( toName = = NULL ) ) {
2005-04-17 02:20:36 +04:00
rc = - ENOMEM ;
goto cifs_hl_exit ;
}
2007-07-19 03:21:09 +04:00
/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/
if ( pTcon - > unix_ext )
2005-04-17 02:20:36 +04:00
rc = CIFSUnixCreateHardLink ( xid , pTcon , fromName , toName ,
2007-07-10 05:16:18 +04:00
cifs_sb_target - > local_nls ,
2005-04-29 09:41:06 +04:00
cifs_sb_target - > mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
2005-04-17 02:20:36 +04:00
else {
rc = CIFSCreateHardLink ( xid , pTcon , fromName , toName ,
2007-07-10 05:16:18 +04:00
cifs_sb_target - > local_nls ,
2005-04-29 09:41:06 +04:00
cifs_sb_target - > mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
2007-07-10 05:16:18 +04:00
if ( ( rc = = - EIO ) | | ( rc = = - EINVAL ) )
rc = - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
2006-11-16 23:54:20 +03:00
d_drop ( direntry ) ; /* force new lookup from server of target */
/* if source file is cached (oplocked) revalidate will not go to server
until the file is closed or oplock broken so update nlinks locally */
2007-07-10 05:16:18 +04:00
if ( old_file - > d_inode ) {
2006-11-16 23:54:20 +03:00
cifsInode = CIFS_I ( old_file - > d_inode ) ;
2007-07-10 05:16:18 +04:00
if ( rc = = 0 ) {
2006-11-16 23:54:20 +03:00
old_file - > d_inode - > i_nlink + + ;
2007-02-17 07:30:54 +03:00
/* BB should we make this contingent on superblock flag NOATIME? */
/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
2006-11-16 23:54:20 +03:00
/* parent dir timestamps will update from srv
within a second , would it really be worth it
to set the parent dir cifs inode time to zero
to force revalidate ( faster ) for it too ? */
}
2007-07-10 05:16:18 +04:00
/* if not oplocked will force revalidate to get info
2006-11-16 23:54:20 +03:00
on source file from srv */
cifsInode - > time = 0 ;
2007-07-10 05:16:18 +04:00
/* Will update parent dir timestamps from srv within a second.
2006-11-16 23:54:20 +03:00
Would it really be worth it to set the parent dir ( cifs
inode ) time field to zero to force revalidate on parent
2007-07-10 05:16:18 +04:00
directory faster ie
2006-11-16 23:54:20 +03:00
CIFS_I ( inode ) - > time = 0 ; */
2005-04-17 02:20:36 +04:00
}
cifs_hl_exit :
2005-11-07 12:01:34 +03:00
kfree ( fromName ) ;
kfree ( toName ) ;
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
return rc ;
}
2005-08-20 05:02:56 +04:00
void *
2005-04-17 02:20:36 +04:00
cifs_follow_link ( struct dentry * direntry , struct nameidata * nd )
{
struct inode * inode = direntry - > d_inode ;
2009-05-19 17:57:03 +04:00
int rc = - ENOMEM ;
2005-04-17 02:20:36 +04:00
int xid ;
char * full_path = NULL ;
2009-05-19 17:57:03 +04:00
char * target_path = NULL ;
struct cifs_sb_info * cifs_sb = CIFS_SB ( inode - > i_sb ) ;
struct cifsTconInfo * tcon = cifs_sb - > tcon ;
2005-04-17 02:20:36 +04:00
xid = GetXid ( ) ;
2009-05-19 17:57:03 +04:00
/*
* For now , we just handle symlinks with unix extensions enabled .
* Eventually we should handle NTFS reparse points , and MacOS
* symlink support . For instance . . .
*
* rc = CIFSSMBQueryReparseLinkInfo ( . . . )
*
* For now , just return - EACCES when the server doesn ' t support posix
* extensions . Note that we still allow querying symlinks when posix
* extensions are manually disabled . We could disable these as well
* but there doesn ' t seem to be any harm in allowing the client to
* read them .
*/
if ( ! ( tcon - > ses - > capabilities & CAP_UNIX ) ) {
rc = - EACCES ;
goto out ;
}
2005-04-17 02:20:36 +04:00
2009-05-19 17:57:03 +04:00
full_path = build_path_from_dentry ( direntry ) ;
2005-04-17 02:20:36 +04:00
if ( ! full_path )
2009-04-30 15:17:56 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
cFYI ( 1 , ( " Full path: %s inode = 0x%p " , full_path , inode ) ) ;
2009-05-19 17:57:03 +04:00
rc = CIFSSMBUnixQuerySymLink ( xid , tcon , full_path , & target_path ,
cifs_sb - > local_nls ) ;
kfree ( full_path ) ;
out :
2009-04-30 15:17:56 +04:00
if ( rc ! = 0 ) {
2005-04-17 02:20:36 +04:00
kfree ( target_path ) ;
target_path = ERR_PTR ( rc ) ;
}
FreeXid ( xid ) ;
nd_set_link ( nd , target_path ) ;
2009-04-30 15:17:56 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
int
cifs_symlink ( struct inode * inode , struct dentry * direntry , const char * symname )
{
int rc = - EOPNOTSUPP ;
int xid ;
struct cifs_sb_info * cifs_sb ;
struct cifsTconInfo * pTcon ;
char * full_path = NULL ;
struct inode * newinode = NULL ;
xid = GetXid ( ) ;
cifs_sb = CIFS_SB ( inode - > i_sb ) ;
pTcon = cifs_sb - > tcon ;
2005-08-30 22:32:14 +04:00
full_path = build_path_from_dentry ( direntry ) ;
2005-04-17 02:20:36 +04:00
2007-07-10 05:16:18 +04:00
if ( full_path = = NULL ) {
2009-06-25 16:42:34 +04:00
rc = - ENOMEM ;
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
2009-06-25 16:42:34 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2006-05-31 22:05:34 +04:00
cFYI ( 1 , ( " Full path: %s " , full_path ) ) ;
2005-04-17 02:20:36 +04:00
cFYI ( 1 , ( " symname is %s " , symname ) ) ;
/* BB what if DFS and this volume is on different share? BB */
2007-07-19 03:21:09 +04:00
if ( pTcon - > unix_ext )
2005-04-17 02:20:36 +04:00
rc = CIFSUnixCreateSymLink ( xid , pTcon , full_path , symname ,
cifs_sb - > local_nls ) ;
/* else
2007-07-10 05:16:18 +04:00
rc = CIFSCreateReparseSymLink ( xid , pTcon , fromName , toName ,
cifs_sb_target - > local_nls ) ; */
2005-04-17 02:20:36 +04:00
if ( rc = = 0 ) {
2007-07-19 03:21:09 +04:00
if ( pTcon - > unix_ext )
2005-04-17 02:20:36 +04:00
rc = cifs_get_inode_info_unix ( & newinode , full_path ,
2007-07-10 05:16:18 +04:00
inode - > i_sb , xid ) ;
2005-04-17 02:20:36 +04:00
else
rc = cifs_get_inode_info ( & newinode , full_path , NULL ,
2008-03-15 01:37:16 +03:00
inode - > i_sb , xid , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( rc ! = 0 ) {
2006-05-31 22:05:34 +04:00
cFYI ( 1 , ( " Create symlink ok, getinodeinfo fail rc = %d " ,
2005-04-17 02:20:36 +04:00
rc ) ) ;
} else {
2005-08-23 07:09:43 +04:00
if ( pTcon - > nocase )
direntry - > d_op = & cifs_ci_dentry_ops ;
else
direntry - > d_op = & cifs_dentry_ops ;
2005-04-17 02:20:36 +04:00
d_instantiate ( direntry , newinode ) ;
}
}
2005-11-07 12:01:34 +03:00
kfree ( full_path ) ;
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
return rc ;
}
2005-08-20 05:02:56 +04:00
void cifs_put_link ( struct dentry * direntry , struct nameidata * nd , void * cookie )
2005-04-17 02:20:36 +04:00
{
char * p = nd_get_link ( nd ) ;
if ( ! IS_ERR ( p ) )
kfree ( p ) ;
}