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