linux/fs/cifs/link.c
<<
>>
Prefs
   1/*
   2 *   fs/cifs/link.c
   3 *
   4 *   Copyright (C) International Business Machines  Corp., 2002,2008
   5 *   Author(s): Steve French (sfrench@us.ibm.com)
   6 *
   7 *   This library is free software; you can redistribute it and/or modify
   8 *   it under the terms of the GNU Lesser General Public License as published
   9 *   by the Free Software Foundation; either version 2.1 of the License, or
  10 *   (at your option) any later version.
  11 *
  12 *   This library is distributed in the hope that it will be useful,
  13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  15 *   the GNU Lesser General Public License for more details.
  16 *
  17 *   You should have received a copy of the GNU Lesser General Public License
  18 *   along with this library; if not, write to the Free Software
  19 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20 */
  21#include <linux/fs.h>
  22#include <linux/stat.h>
  23#include <linux/namei.h>
  24#include "cifsfs.h"
  25#include "cifspdu.h"
  26#include "cifsglob.h"
  27#include "cifsproto.h"
  28#include "cifs_debug.h"
  29#include "cifs_fs_sb.h"
  30
  31int
  32cifs_hardlink(struct dentry *old_file, struct inode *inode,
  33              struct dentry *direntry)
  34{
  35        int rc = -EACCES;
  36        int xid;
  37        char *fromName = NULL;
  38        char *toName = NULL;
  39        struct cifs_sb_info *cifs_sb_target;
  40        struct cifsTconInfo *pTcon;
  41        struct cifsInodeInfo *cifsInode;
  42
  43        xid = GetXid();
  44
  45        cifs_sb_target = CIFS_SB(inode->i_sb);
  46        pTcon = cifs_sb_target->tcon;
  47
  48/* No need to check for cross device links since server will do that
  49   BB note DFS case in future though (when we may have to check) */
  50
  51        fromName = build_path_from_dentry(old_file);
  52        toName = build_path_from_dentry(direntry);
  53        if ((fromName == NULL) || (toName == NULL)) {
  54                rc = -ENOMEM;
  55                goto cifs_hl_exit;
  56        }
  57
  58/*      if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/
  59        if (pTcon->unix_ext)
  60                rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
  61                                            cifs_sb_target->local_nls,
  62                                            cifs_sb_target->mnt_cifs_flags &
  63                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
  64        else {
  65                rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
  66                                        cifs_sb_target->local_nls,
  67                                        cifs_sb_target->mnt_cifs_flags &
  68                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
  69                if ((rc == -EIO) || (rc == -EINVAL))
  70                        rc = -EOPNOTSUPP;
  71        }
  72
  73        d_drop(direntry);       /* force new lookup from server of target */
  74
  75        /* if source file is cached (oplocked) revalidate will not go to server
  76           until the file is closed or oplock broken so update nlinks locally */
  77        if (old_file->d_inode) {
  78                cifsInode = CIFS_I(old_file->d_inode);
  79                if (rc == 0) {
  80                        old_file->d_inode->i_nlink++;
  81/* BB should we make this contingent on superblock flag NOATIME? */
  82/*                      old_file->d_inode->i_ctime = CURRENT_TIME;*/
  83                        /* parent dir timestamps will update from srv
  84                        within a second, would it really be worth it
  85                        to set the parent dir cifs inode time to zero
  86                        to force revalidate (faster) for it too? */
  87                }
  88                /* if not oplocked will force revalidate to get info
  89                   on source file from srv */
  90                cifsInode->time = 0;
  91
  92                /* Will update parent dir timestamps from srv within a second.
  93                   Would it really be worth it to set the parent dir (cifs
  94                   inode) time field to zero to force revalidate on parent
  95                   directory faster ie
  96                        CIFS_I(inode)->time = 0;  */
  97        }
  98
  99cifs_hl_exit:
 100        kfree(fromName);
 101        kfree(toName);
 102        FreeXid(xid);
 103        return rc;
 104}
 105
 106void *
 107cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
 108{
 109        struct inode *inode = direntry->d_inode;
 110        int rc = -ENOMEM;
 111        int xid;
 112        char *full_path = NULL;
 113        char *target_path = NULL;
 114        struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
 115        struct cifsTconInfo *tcon = cifs_sb->tcon;
 116
 117        xid = GetXid();
 118
 119        /*
 120         * For now, we just handle symlinks with unix extensions enabled.
 121         * Eventually we should handle NTFS reparse points, and MacOS
 122         * symlink support. For instance...
 123         *
 124         * rc = CIFSSMBQueryReparseLinkInfo(...)
 125         *
 126         * For now, just return -EACCES when the server doesn't support posix
 127         * extensions. Note that we still allow querying symlinks when posix
 128         * extensions are manually disabled. We could disable these as well
 129         * but there doesn't seem to be any harm in allowing the client to
 130         * read them.
 131         */
 132        if (!(tcon->ses->capabilities & CAP_UNIX)) {
 133                rc = -EACCES;
 134                goto out;
 135        }
 136
 137        full_path = build_path_from_dentry(direntry);
 138        if (!full_path)
 139                goto out;
 140
 141        cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode));
 142
 143        rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
 144                                     cifs_sb->local_nls);
 145        kfree(full_path);
 146out:
 147        if (rc != 0) {
 148                kfree(target_path);
 149                target_path = ERR_PTR(rc);
 150        }
 151
 152        FreeXid(xid);
 153        nd_set_link(nd, target_path);
 154        return NULL;
 155}
 156
 157int
 158cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
 159{
 160        int rc = -EOPNOTSUPP;
 161        int xid;
 162        struct cifs_sb_info *cifs_sb;
 163        struct cifsTconInfo *pTcon;
 164        char *full_path = NULL;
 165        struct inode *newinode = NULL;
 166
 167        xid = GetXid();
 168
 169        cifs_sb = CIFS_SB(inode->i_sb);
 170        pTcon = cifs_sb->tcon;
 171
 172        full_path = build_path_from_dentry(direntry);
 173
 174        if (full_path == NULL) {
 175                rc = -ENOMEM;
 176                FreeXid(xid);
 177                return rc;
 178        }
 179
 180        cFYI(1, ("Full path: %s", full_path));
 181        cFYI(1, ("symname is %s", symname));
 182
 183        /* BB what if DFS and this volume is on different share? BB */
 184        if (pTcon->unix_ext)
 185                rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
 186                                           cifs_sb->local_nls);
 187        /* else
 188           rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
 189                                        cifs_sb_target->local_nls); */
 190
 191        if (rc == 0) {
 192                if (pTcon->unix_ext)
 193                        rc = cifs_get_inode_info_unix(&newinode, full_path,
 194                                                      inode->i_sb, xid);
 195                else
 196                        rc = cifs_get_inode_info(&newinode, full_path, NULL,
 197                                                 inode->i_sb, xid, NULL);
 198
 199                if (rc != 0) {
 200                        cFYI(1, ("Create symlink ok, getinodeinfo fail rc = %d",
 201                              rc));
 202                } else {
 203                        if (pTcon->nocase)
 204                                direntry->d_op = &cifs_ci_dentry_ops;
 205                        else
 206                                direntry->d_op = &cifs_dentry_ops;
 207                        d_instantiate(direntry, newinode);
 208                }
 209        }
 210
 211        kfree(full_path);
 212        FreeXid(xid);
 213        return rc;
 214}
 215
 216void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
 217{
 218        char *p = nd_get_link(nd);
 219        if (!IS_ERR(p))
 220                kfree(p);
 221}
 222