[CIFS] Allow disabling CIFS Unix Extensions as mount option
[deliverable/linux.git] / fs / cifs / link.c
CommitLineData
1da177e4
LT
1/*
2 * fs/cifs/link.c
3 *
4 * Copyright (C) International Business Machines Corp., 2002,2003
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
7f57356b
SF
51 fromName = build_path_from_dentry(old_file);
52 toName = build_path_from_dentry(direntry);
fb8c4b14 53 if ((fromName == NULL) || (toName == NULL)) {
1da177e4
LT
54 rc = -ENOMEM;
55 goto cifs_hl_exit;
56 }
57
c18c842b
SF
58/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/
59 if (pTcon->unix_ext)
1da177e4 60 rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
fb8c4b14 61 cifs_sb_target->local_nls,
737b758c
SF
62 cifs_sb_target->mnt_cifs_flags &
63 CIFS_MOUNT_MAP_SPECIAL_CHR);
1da177e4
LT
64 else {
65 rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
fb8c4b14 66 cifs_sb_target->local_nls,
737b758c
SF
67 cifs_sb_target->mnt_cifs_flags &
68 CIFS_MOUNT_MAP_SPECIAL_CHR);
fb8c4b14
SF
69 if ((rc == -EIO) || (rc == -EINVAL))
70 rc = -EOPNOTSUPP;
1da177e4
LT
71 }
72
31ec35d6
SF
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 */
fb8c4b14 77 if (old_file->d_inode) {
31ec35d6 78 cifsInode = CIFS_I(old_file->d_inode);
fb8c4b14 79 if (rc == 0) {
31ec35d6 80 old_file->d_inode->i_nlink++;
1b2b2126
SF
81/* BB should we make this contingent on superblock flag NOATIME? */
82/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
31ec35d6
SF
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 }
fb8c4b14 88 /* if not oplocked will force revalidate to get info
31ec35d6
SF
89 on source file from srv */
90 cifsInode->time = 0;
91
fb8c4b14 92 /* Will update parent dir timestamps from srv within a second.
31ec35d6
SF
93 Would it really be worth it to set the parent dir (cifs
94 inode) time field to zero to force revalidate on parent
fb8c4b14 95 directory faster ie
31ec35d6 96 CIFS_I(inode)->time = 0; */
1da177e4 97 }
1da177e4
LT
98
99cifs_hl_exit:
f99d49ad
JJ
100 kfree(fromName);
101 kfree(toName);
1da177e4
LT
102 FreeXid(xid);
103 return rc;
104}
105
cc314eef 106void *
1da177e4
LT
107cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
108{
109 struct inode *inode = direntry->d_inode;
110 int rc = -EACCES;
111 int xid;
112 char *full_path = NULL;
fb8c4b14 113 char *target_path = ERR_PTR(-ENOMEM);
1da177e4
LT
114 struct cifs_sb_info *cifs_sb;
115 struct cifsTconInfo *pTcon;
116
117 xid = GetXid();
118
7f57356b 119 full_path = build_path_from_dentry(direntry);
1da177e4
LT
120
121 if (!full_path)
122 goto out_no_free;
123
124 cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode));
7f57356b
SF
125 cifs_sb = CIFS_SB(inode->i_sb);
126 pTcon = cifs_sb->tcon;
1da177e4
LT
127 target_path = kmalloc(PATH_MAX, GFP_KERNEL);
128 if (!target_path) {
129 target_path = ERR_PTR(-ENOMEM);
130 goto out;
131 }
132
c18c842b
SF
133 /* We could change this to:
134 if (pTcon->unix_ext)
135 but there does not seem any point in refusing to
136 get symlink info if we can, even if unix extensions
137 turned off for this mount */
138
1da177e4
LT
139 if (pTcon->ses->capabilities & CAP_UNIX)
140 rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
141 target_path,
142 PATH_MAX-1,
143 cifs_sb->local_nls);
144 else {
c18c842b 145 /* BB add read reparse point symlink code here */
1da177e4
LT
146 /* rc = CIFSSMBQueryReparseLinkInfo */
147 /* BB Add code to Query ReparsePoint info */
148 /* BB Add MAC style xsymlink check here if enabled */
149 }
150
151 if (rc == 0) {
152
153/* BB Add special case check for Samba DFS symlinks */
154
155 target_path[PATH_MAX-1] = 0;
156 } else {
157 kfree(target_path);
158 target_path = ERR_PTR(rc);
159 }
160
161out:
162 kfree(full_path);
163out_no_free:
164 FreeXid(xid);
165 nd_set_link(nd, target_path);
cc314eef 166 return NULL; /* No cookie */
1da177e4
LT
167}
168
169int
170cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
171{
172 int rc = -EOPNOTSUPP;
173 int xid;
174 struct cifs_sb_info *cifs_sb;
175 struct cifsTconInfo *pTcon;
176 char *full_path = NULL;
177 struct inode *newinode = NULL;
178
179 xid = GetXid();
180
181 cifs_sb = CIFS_SB(inode->i_sb);
182 pTcon = cifs_sb->tcon;
183
7f57356b 184 full_path = build_path_from_dentry(direntry);
1da177e4 185
fb8c4b14 186 if (full_path == NULL) {
1da177e4
LT
187 FreeXid(xid);
188 return -ENOMEM;
189 }
190
26a21b98 191 cFYI(1, ("Full path: %s", full_path));
1da177e4
LT
192 cFYI(1, ("symname is %s", symname));
193
194 /* BB what if DFS and this volume is on different share? BB */
c18c842b 195 if (pTcon->unix_ext)
1da177e4
LT
196 rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
197 cifs_sb->local_nls);
198 /* else
fb8c4b14
SF
199 rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
200 cifs_sb_target->local_nls); */
1da177e4
LT
201
202 if (rc == 0) {
c18c842b 203 if (pTcon->unix_ext)
1da177e4 204 rc = cifs_get_inode_info_unix(&newinode, full_path,
fb8c4b14 205 inode->i_sb, xid);
1da177e4
LT
206 else
207 rc = cifs_get_inode_info(&newinode, full_path, NULL,
fb8c4b14 208 inode->i_sb, xid);
1da177e4
LT
209
210 if (rc != 0) {
26a21b98 211 cFYI(1, ("Create symlink ok, getinodeinfo fail rc = %d",
1da177e4
LT
212 rc));
213 } else {
b92327fe
SF
214 if (pTcon->nocase)
215 direntry->d_op = &cifs_ci_dentry_ops;
216 else
217 direntry->d_op = &cifs_dentry_ops;
1da177e4
LT
218 d_instantiate(direntry, newinode);
219 }
220 }
221
f99d49ad 222 kfree(full_path);
1da177e4
LT
223 FreeXid(xid);
224 return rc;
225}
226
227int
228cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
229{
230 struct inode *inode = direntry->d_inode;
231 int rc = -EACCES;
232 int xid;
233 int oplock = FALSE;
234 struct cifs_sb_info *cifs_sb;
235 struct cifsTconInfo *pTcon;
236 char *full_path = NULL;
fb8c4b14
SF
237 char *tmp_path = NULL;
238 char *tmpbuffer;
239 unsigned char *referrals = NULL;
1da177e4
LT
240 int num_referrals = 0;
241 int len;
242 __u16 fid;
243
244 xid = GetXid();
245 cifs_sb = CIFS_SB(inode->i_sb);
246 pTcon = cifs_sb->tcon;
247
fb8c4b14 248/* BB would it be safe against deadlock to grab this sem
1da177e4 249 even though rename itself grabs the sem and calls lookup? */
a11f3a05 250/* mutex_lock(&inode->i_sb->s_vfs_rename_mutex);*/
7f57356b 251 full_path = build_path_from_dentry(direntry);
a11f3a05 252/* mutex_unlock(&inode->i_sb->s_vfs_rename_mutex);*/
1da177e4 253
fb8c4b14 254 if (full_path == NULL) {
1da177e4
LT
255 FreeXid(xid);
256 return -ENOMEM;
257 }
258
259 cFYI(1,
260 ("Full path: %s inode = 0x%p pBuffer = 0x%p buflen = %d",
261 full_path, inode, pBuffer, buflen));
fb8c4b14 262 if (buflen > PATH_MAX)
1da177e4
LT
263 len = PATH_MAX;
264 else
265 len = buflen;
fb8c4b14
SF
266 tmpbuffer = kmalloc(len, GFP_KERNEL);
267 if (tmpbuffer == NULL) {
f99d49ad 268 kfree(full_path);
1da177e4
LT
269 FreeXid(xid);
270 return -ENOMEM;
271 }
272
fb8c4b14
SF
273/* BB add read reparse point symlink code and
274 Unix extensions symlink code here BB */
c18c842b 275/* We could disable this based on pTcon->unix_ext flag instead ... but why? */
1da177e4
LT
276 if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
277 rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
278 tmpbuffer,
279 len - 1,
280 cifs_sb->local_nls);
1bd5bbcb 281 else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
fb8c4b14 282 cERROR(1, ("SFU style symlinks not implemented yet"));
1bd5bbcb 283 /* add open and read as in fs/cifs/inode.c */
1bd5bbcb 284 } else {
1da177e4 285 rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ,
fb8c4b14
SF
286 OPEN_REPARSE_POINT, &fid, &oplock, NULL,
287 cifs_sb->local_nls,
288 cifs_sb->mnt_cifs_flags &
737b758c 289 CIFS_MOUNT_MAP_SPECIAL_CHR);
fb8c4b14 290 if (!rc) {
1da177e4
LT
291 rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path,
292 tmpbuffer,
fb8c4b14 293 len - 1,
1da177e4
LT
294 fid,
295 cifs_sb->local_nls);
fb8c4b14 296 if (CIFSSMBClose(xid, pTcon, fid)) {
63135e08
SF
297 cFYI(1, ("Error closing junction point "
298 "(open for ioctl)"));
1da177e4 299 }
fb8c4b14 300 if (rc == -EIO) {
1da177e4
LT
301 /* Query if DFS Junction */
302 tmp_path =
303 kmalloc(MAX_TREE_SIZE + MAX_PATHCONF + 1,
304 GFP_KERNEL);
305 if (tmp_path) {
fb8c4b14
SF
306 strncpy(tmp_path, pTcon->treeName,
307 MAX_TREE_SIZE);
308 strncat(tmp_path, full_path,
309 MAX_PATHCONF);
310 rc = get_dfs_path(xid, pTcon->ses,
311 tmp_path,
737b758c
SF
312 cifs_sb->local_nls,
313 &num_referrals, &referrals,
314 cifs_sb->mnt_cifs_flags &
315 CIFS_MOUNT_MAP_SPECIAL_CHR);
fb8c4b14
SF
316 cFYI(1, ("Get DFS for %s rc = %d ",
317 tmp_path, rc));
318 if ((num_referrals == 0) && (rc == 0))
1da177e4
LT
319 rc = -EACCES;
320 else {
fb8c4b14
SF
321 cFYI(1, ("num referral: %d",
322 num_referrals));
323 if (referrals) {
324 cFYI(1,("referral string: %s", referrals));
50c2f753
SF
325 strncpy(tmpbuffer,
326 referrals,
327 len-1);
1da177e4
LT
328 }
329 }
f99d49ad 330 kfree(referrals);
1da177e4
LT
331 kfree(tmp_path);
332}
fb8c4b14
SF
333 /* BB add code like else decode referrals
334 then memcpy to tmpbuffer and free referrals
335 string array BB */
1da177e4
LT
336 }
337 }
338 }
339 /* BB Anything else to do to handle recursive links? */
340 /* BB Should we be using page ops here? */
341
342 /* BB null terminate returned string in pBuffer? BB */
343 if (rc == 0) {
344 rc = vfs_readlink(direntry, pBuffer, len, tmpbuffer);
345 cFYI(1,
346 ("vfs_readlink called from cifs_readlink returned %d",
347 rc));
348 }
349
f99d49ad
JJ
350 kfree(tmpbuffer);
351 kfree(full_path);
1da177e4
LT
352 FreeXid(xid);
353 return rc;
354}
355
cc314eef 356void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
1da177e4
LT
357{
358 char *p = nd_get_link(nd);
359 if (!IS_ERR(p))
360 kfree(p);
361}
This page took 0.17999 seconds and 5 git commands to generate.