fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / cifs / link.c
index 8d1c2cb..8e25996 100644 (file)
@@ -20,6 +20,7 @@
  */
 #include <linux/fs.h>
 #include <linux/stat.h>
+#include <linux/namei.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
 #include "cifsglob.h"
@@ -47,10 +48,8 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
 /* 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;
@@ -58,68 +57,76 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
 
        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)
@@ -130,25 +137,25 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
        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
@@ -166,16 +173,14 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
        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 */
@@ -194,17 +199,18 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
                                                 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;
 }
@@ -232,9 +238,9 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
 
 /* 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);
@@ -250,8 +256,7 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
                len = buflen;
        tmpbuffer = kmalloc(len,GFP_KERNEL);   
        if(tmpbuffer == NULL) {
-               if (full_path)
-                       kfree(full_path);
+               kfree(full_path);
                FreeXid(xid);
                return -ENOMEM;
        }
@@ -262,9 +267,16 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
                                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,
@@ -283,24 +295,23 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
                                        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 */
                        }
@@ -317,12 +328,15 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
                      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);
+}