*/
#include <linux/fs.h>
#include <linux/stat.h>
+#include <linux/namei.h>
#include "cifsfs.h"
#include "cifspdu.h"
#include "cifsglob.h"
/* 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) */
- down(&inode->i_sb->s_vfs_rename_sem);
fromName = build_path_from_dentry(old_file);
toName = build_path_from_dentry(direntry);
- up(&inode->i_sb->s_vfs_rename_sem);
if((fromName == NULL) || (toName == NULL)) {
rc = -ENOMEM;
goto cifs_hl_exit;
if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)
rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
- cifs_sb_target->local_nls);
+ cifs_sb_target->local_nls,
+ cifs_sb_target->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
else {
rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
- cifs_sb_target->local_nls);
- if(rc == -EIO)
+ cifs_sb_target->local_nls,
+ cifs_sb_target->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if((rc == -EIO) || (rc == -EINVAL))
rc = -EOPNOTSUPP;
}
-/* if (!rc) */
- {
- /* renew_parental_timestamps(old_file);
- inode->i_nlink++;
- mark_inode_dirty(inode);
- d_instantiate(direntry, inode); */
- /* BB add call to either mark inode dirty or refresh its data and timestamp to current time */
+ 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 */
+ if(old_file->d_inode) {
+ cifsInode = CIFS_I(old_file->d_inode);
+ if(rc == 0) {
+ old_file->d_inode->i_nlink++;
+ old_file->d_inode->i_ctime = CURRENT_TIME;
+ /* 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? */
+ }
+ /* if not oplocked will force revalidate to get info
+ on source file from srv */
+ cifsInode->time = 0;
+
+ /* Will update parent dir timestamps from srv within a second.
+ Would it really be worth it to set the parent dir (cifs
+ inode) time field to zero to force revalidate on parent
+ directory faster ie
+ CIFS_I(inode)->time = 0; */
}
- d_drop(direntry); /* force new lookup from server */
- cifsInode = CIFS_I(old_file->d_inode);
- cifsInode->time = 0; /* will force revalidate to go get info when needed */
cifs_hl_exit:
- if (fromName)
- kfree(fromName);
- if (toName)
- kfree(toName);
+ kfree(fromName);
+ kfree(toName);
FreeXid(xid);
return rc;
}
-int
+void *
cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
{
struct inode *inode = direntry->d_inode;
int rc = -EACCES;
int xid;
char *full_path = NULL;
- char * target_path;
+ char * target_path = ERR_PTR(-ENOMEM);
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
xid = GetXid();
- down(&direntry->d_sb->s_vfs_rename_sem);
full_path = build_path_from_dentry(direntry);
- up(&direntry->d_sb->s_vfs_rename_sem);
- if(full_path == NULL) {
- FreeXid(xid);
- return -ENOMEM;
- }
+ if (!full_path)
+ goto out_no_free;
+
cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode));
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
target_path = kmalloc(PATH_MAX, GFP_KERNEL);
- if(target_path == NULL) {
- if (full_path)
- kfree(full_path);
- FreeXid(xid);
- return -ENOMEM;
+ if (!target_path) {
+ target_path = ERR_PTR(-ENOMEM);
+ goto out;
}
- /* can not call the following line due to EFAULT in vfs_readlink which is presumably expecting a user space buffer */
- /* length = cifs_readlink(direntry,target_path, sizeof(target_path) - 1); */
/* BB add read reparse point symlink code and Unix extensions symlink code here BB */
if (pTcon->ses->capabilities & CAP_UNIX)
else {
/* rc = CIFSSMBQueryReparseLinkInfo */
/* BB Add code to Query ReparsePoint info */
+ /* BB Add MAC style xsymlink check here if enabled */
}
- /* BB Anything else to do to handle recursive links? */
- /* BB Should we be using page symlink ops here? */
if (rc == 0) {
/* BB Add special case check for Samba DFS symlinks */
target_path[PATH_MAX-1] = 0;
- rc = vfs_follow_link(nd, target_path);
+ } else {
+ kfree(target_path);
+ target_path = ERR_PTR(rc);
}
- /* else EACCESS */
- if (target_path)
- kfree(target_path);
- if (full_path)
- kfree(full_path);
+out:
+ kfree(full_path);
+out_no_free:
FreeXid(xid);
- return rc;
+ nd_set_link(nd, target_path);
+ return NULL; /* No cookie */
}
int
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
- down(&inode->i_sb->s_vfs_rename_sem);
full_path = build_path_from_dentry(direntry);
- up(&inode->i_sb->s_vfs_rename_sem);
if(full_path == NULL) {
FreeXid(xid);
return -ENOMEM;
}
- cFYI(1, ("Full path: %s ", full_path));
+ cFYI(1, ("Full path: %s", full_path));
cFYI(1, ("symname is %s", symname));
/* BB what if DFS and this volume is on different share? BB */
inode->i_sb,xid);
if (rc != 0) {
- cFYI(1,
- ("Create symlink worked but get_inode_info failed with rc = %d ",
+ cFYI(1, ("Create symlink ok, getinodeinfo fail rc = %d",
rc));
} else {
- direntry->d_op = &cifs_dentry_ops;
+ if (pTcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
d_instantiate(direntry, newinode);
}
}
- if (full_path)
- kfree(full_path);
+ kfree(full_path);
FreeXid(xid);
return rc;
}
/* BB would it be safe against deadlock to grab this sem
even though rename itself grabs the sem and calls lookup? */
-/* down(&inode->i_sb->s_vfs_rename_sem);*/
+/* mutex_lock(&inode->i_sb->s_vfs_rename_mutex);*/
full_path = build_path_from_dentry(direntry);
-/* up(&inode->i_sb->s_vfs_rename_sem);*/
+/* mutex_unlock(&inode->i_sb->s_vfs_rename_mutex);*/
if(full_path == NULL) {
FreeXid(xid);
len = buflen;
tmpbuffer = kmalloc(len,GFP_KERNEL);
if(tmpbuffer == NULL) {
- if (full_path)
- kfree(full_path);
+ kfree(full_path);
FreeXid(xid);
return -ENOMEM;
}
tmpbuffer,
len - 1,
cifs_sb->local_nls);
- else {
+ else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ cERROR(1,("SFU style symlinks not implemented yet"));
+ /* add open and read as in fs/cifs/inode.c */
+
+ } else {
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ,
- OPEN_REPARSE_POINT,&fid, &oplock, NULL, cifs_sb->local_nls);
+ OPEN_REPARSE_POINT,&fid, &oplock, NULL,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
if(!rc) {
rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path,
tmpbuffer,
strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
strncat(tmp_path, full_path, MAX_PATHCONF);
rc = get_dfs_path(xid, pTcon->ses, tmp_path,
- cifs_sb->local_nls, &num_referrals, &referrals);
+ cifs_sb->local_nls,
+ &num_referrals, &referrals,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
cFYI(1,("Get DFS for %s rc = %d ",tmp_path, rc));
if((num_referrals == 0) && (rc == 0))
rc = -EACCES;
else {
cFYI(1,("num referral: %d",num_referrals));
if(referrals) {
- cFYI(1,("referral string: %s ",referrals));
+ cFYI(1,("referral string: %s",referrals));
strncpy(tmpbuffer, referrals, len-1);
}
}
- if(referrals)
- kfree(referrals);
+ kfree(referrals);
kfree(tmp_path);
- if(referrals) {
- kfree(referrals);
- }
- }
+}
/* BB add code like else decode referrals then memcpy to
tmpbuffer and free referrals string array BB */
}
rc));
}
- if (tmpbuffer) {
- kfree(tmpbuffer);
- }
- if (full_path) {
- kfree(full_path);
- }
+ kfree(tmpbuffer);
+ kfree(full_path);
FreeXid(xid);
return rc;
}
+
+void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
+{
+ char *p = nd_get_link(nd);
+ if (!IS_ERR(p))
+ kfree(p);
+}