cifs: implement CIFSCouldBeMFSymlink() and CIFSCheckMFSymlink()
[deliverable/linux.git] / fs / cifs / link.c
CommitLineData
1da177e4
LT
1/*
2 * fs/cifs/link.c
3 *
366781c1 4 * Copyright (C) International Business Machines Corp., 2002,2008
1da177e4
LT
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>
5a0e3ad6 23#include <linux/slab.h>
1da177e4
LT
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"
c69c1b6e
SM
31#include "md5.h"
32
33#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)
34#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))
35#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))
36#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)
37#define CIFS_MF_SYMLINK_FILE_SIZE \
38 (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)
39
40#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"
41#define CIFS_MF_SYMLINK_MD5_FORMAT \
42 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n"
43#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) \
44 md5_hash[0], md5_hash[1], md5_hash[2], md5_hash[3], \
45 md5_hash[4], md5_hash[5], md5_hash[6], md5_hash[7], \
46 md5_hash[8], md5_hash[9], md5_hash[10], md5_hash[11],\
47 md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15]
48
49static int
50CIFSParseMFSymlink(const u8 *buf,
51 unsigned int buf_len,
52 unsigned int *_link_len,
53 char **_link_str)
54{
55 int rc;
56 unsigned int link_len;
57 const char *md5_str1;
58 const char *link_str;
59 struct MD5Context md5_ctx;
60 u8 md5_hash[16];
61 char md5_str2[34];
62
63 if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
64 return -EINVAL;
65
66 md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
67 link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];
68
69 rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
70 if (rc != 1)
71 return -EINVAL;
72
73 cifs_MD5_init(&md5_ctx);
74 cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len);
75 cifs_MD5_final(md5_hash, &md5_ctx);
76
77 snprintf(md5_str2, sizeof(md5_str2),
78 CIFS_MF_SYMLINK_MD5_FORMAT,
79 CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
80
81 if (strncmp(md5_str1, md5_str2, 17) != 0)
82 return -EINVAL;
83
84 if (_link_str) {
85 *_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
86 if (!*_link_str)
87 return -ENOMEM;
88 }
89
90 *_link_len = link_len;
91 return 0;
92}
1da177e4 93
8bfb50a8
SM
94bool
95CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr)
96{
97 if (!(fattr->cf_mode & S_IFREG))
98 /* it's not a symlink */
99 return false;
100
101 if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
102 /* it's not a symlink */
103 return false;
104
105 return true;
106}
107
108int
109CIFSCheckMFSymlink(struct cifs_fattr *fattr,
110 const unsigned char *path,
111 struct cifs_sb_info *cifs_sb, int xid)
112{
113 int rc;
114 int oplock = 0;
115 __u16 netfid = 0;
116 struct cifsTconInfo *pTcon = cifs_sb->tcon;
117 u8 *buf;
118 char *pbuf;
119 unsigned int bytes_read = 0;
120 int buf_type = CIFS_NO_BUFFER;
121 unsigned int link_len = 0;
122 FILE_ALL_INFO file_info;
123
124 if (!CIFSCouldBeMFSymlink(fattr))
125 /* it's not a symlink */
126 return 0;
127
128 rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
129 CREATE_NOT_DIR, &netfid, &oplock, &file_info,
130 cifs_sb->local_nls,
131 cifs_sb->mnt_cifs_flags &
132 CIFS_MOUNT_MAP_SPECIAL_CHR);
133 if (rc != 0)
134 return rc;
135
136 if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) {
137 CIFSSMBClose(xid, pTcon, netfid);
138 /* it's not a symlink */
139 return 0;
140 }
141
142 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
143 if (!buf)
144 return -ENOMEM;
145 pbuf = buf;
146
147 rc = CIFSSMBRead(xid, pTcon, netfid,
148 CIFS_MF_SYMLINK_FILE_SIZE /* length */,
149 0 /* offset */,
150 &bytes_read, &pbuf, &buf_type);
151 CIFSSMBClose(xid, pTcon, netfid);
152 if (rc != 0) {
153 kfree(buf);
154 return rc;
155 }
156
157 rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL);
158 kfree(buf);
159 if (rc == -EINVAL)
160 /* it's not a symlink */
161 return 0;
162 if (rc != 0)
163 return rc;
164
165 /* it is a symlink */
166 fattr->cf_eof = link_len;
167 fattr->cf_mode &= ~S_IFMT;
168 fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
169 fattr->cf_dtype = DT_LNK;
170 return 0;
171}
172
1da177e4
LT
173int
174cifs_hardlink(struct dentry *old_file, struct inode *inode,
175 struct dentry *direntry)
176{
177 int rc = -EACCES;
178 int xid;
179 char *fromName = NULL;
180 char *toName = NULL;
181 struct cifs_sb_info *cifs_sb_target;
182 struct cifsTconInfo *pTcon;
183 struct cifsInodeInfo *cifsInode;
184
185 xid = GetXid();
186
187 cifs_sb_target = CIFS_SB(inode->i_sb);
188 pTcon = cifs_sb_target->tcon;
189
190/* No need to check for cross device links since server will do that
191 BB note DFS case in future though (when we may have to check) */
192
7f57356b
SF
193 fromName = build_path_from_dentry(old_file);
194 toName = build_path_from_dentry(direntry);
fb8c4b14 195 if ((fromName == NULL) || (toName == NULL)) {
1da177e4
LT
196 rc = -ENOMEM;
197 goto cifs_hl_exit;
198 }
199
c18c842b
SF
200/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/
201 if (pTcon->unix_ext)
1da177e4 202 rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
fb8c4b14 203 cifs_sb_target->local_nls,
737b758c
SF
204 cifs_sb_target->mnt_cifs_flags &
205 CIFS_MOUNT_MAP_SPECIAL_CHR);
1da177e4
LT
206 else {
207 rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
fb8c4b14 208 cifs_sb_target->local_nls,
737b758c
SF
209 cifs_sb_target->mnt_cifs_flags &
210 CIFS_MOUNT_MAP_SPECIAL_CHR);
fb8c4b14
SF
211 if ((rc == -EIO) || (rc == -EINVAL))
212 rc = -EOPNOTSUPP;
1da177e4
LT
213 }
214
31ec35d6
SF
215 d_drop(direntry); /* force new lookup from server of target */
216
217 /* if source file is cached (oplocked) revalidate will not go to server
218 until the file is closed or oplock broken so update nlinks locally */
fb8c4b14 219 if (old_file->d_inode) {
31ec35d6 220 cifsInode = CIFS_I(old_file->d_inode);
fb8c4b14 221 if (rc == 0) {
31ec35d6 222 old_file->d_inode->i_nlink++;
1b2b2126
SF
223/* BB should we make this contingent on superblock flag NOATIME? */
224/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
31ec35d6
SF
225 /* parent dir timestamps will update from srv
226 within a second, would it really be worth it
227 to set the parent dir cifs inode time to zero
228 to force revalidate (faster) for it too? */
229 }
fb8c4b14 230 /* if not oplocked will force revalidate to get info
31ec35d6
SF
231 on source file from srv */
232 cifsInode->time = 0;
233
fb8c4b14 234 /* Will update parent dir timestamps from srv within a second.
31ec35d6
SF
235 Would it really be worth it to set the parent dir (cifs
236 inode) time field to zero to force revalidate on parent
fb8c4b14 237 directory faster ie
31ec35d6 238 CIFS_I(inode)->time = 0; */
1da177e4 239 }
1da177e4
LT
240
241cifs_hl_exit:
f99d49ad
JJ
242 kfree(fromName);
243 kfree(toName);
1da177e4
LT
244 FreeXid(xid);
245 return rc;
246}
247
cc314eef 248void *
1da177e4
LT
249cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
250{
251 struct inode *inode = direntry->d_inode;
8b6427a2 252 int rc = -ENOMEM;
1da177e4
LT
253 int xid;
254 char *full_path = NULL;
8b6427a2
JL
255 char *target_path = NULL;
256 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
257 struct cifsTconInfo *tcon = cifs_sb->tcon;
1da177e4
LT
258
259 xid = GetXid();
260
8b6427a2
JL
261 /*
262 * For now, we just handle symlinks with unix extensions enabled.
263 * Eventually we should handle NTFS reparse points, and MacOS
264 * symlink support. For instance...
265 *
266 * rc = CIFSSMBQueryReparseLinkInfo(...)
267 *
268 * For now, just return -EACCES when the server doesn't support posix
269 * extensions. Note that we still allow querying symlinks when posix
270 * extensions are manually disabled. We could disable these as well
271 * but there doesn't seem to be any harm in allowing the client to
272 * read them.
273 */
274 if (!(tcon->ses->capabilities & CAP_UNIX)) {
275 rc = -EACCES;
276 goto out;
277 }
1da177e4 278
8b6427a2 279 full_path = build_path_from_dentry(direntry);
1da177e4 280 if (!full_path)
460b9696 281 goto out;
1da177e4 282
b6b38f70 283 cFYI(1, "Full path: %s inode = 0x%p", full_path, inode);
1da177e4 284
8b6427a2
JL
285 rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
286 cifs_sb->local_nls);
287 kfree(full_path);
288out:
460b9696 289 if (rc != 0) {
1da177e4
LT
290 kfree(target_path);
291 target_path = ERR_PTR(rc);
292 }
293
1da177e4
LT
294 FreeXid(xid);
295 nd_set_link(nd, target_path);
460b9696 296 return NULL;
1da177e4
LT
297}
298
299int
300cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
301{
302 int rc = -EOPNOTSUPP;
303 int xid;
304 struct cifs_sb_info *cifs_sb;
305 struct cifsTconInfo *pTcon;
306 char *full_path = NULL;
307 struct inode *newinode = NULL;
308
309 xid = GetXid();
310
311 cifs_sb = CIFS_SB(inode->i_sb);
312 pTcon = cifs_sb->tcon;
313
7f57356b 314 full_path = build_path_from_dentry(direntry);
1da177e4 315
fb8c4b14 316 if (full_path == NULL) {
0f3bc09e 317 rc = -ENOMEM;
1da177e4 318 FreeXid(xid);
0f3bc09e 319 return rc;
1da177e4
LT
320 }
321
b6b38f70
JP
322 cFYI(1, "Full path: %s", full_path);
323 cFYI(1, "symname is %s", symname);
1da177e4
LT
324
325 /* BB what if DFS and this volume is on different share? BB */
c18c842b 326 if (pTcon->unix_ext)
1da177e4
LT
327 rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
328 cifs_sb->local_nls);
329 /* else
fb8c4b14
SF
330 rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
331 cifs_sb_target->local_nls); */
1da177e4
LT
332
333 if (rc == 0) {
c18c842b 334 if (pTcon->unix_ext)
1da177e4 335 rc = cifs_get_inode_info_unix(&newinode, full_path,
fb8c4b14 336 inode->i_sb, xid);
1da177e4
LT
337 else
338 rc = cifs_get_inode_info(&newinode, full_path, NULL,
8b1327f6 339 inode->i_sb, xid, NULL);
1da177e4
LT
340
341 if (rc != 0) {
b6b38f70
JP
342 cFYI(1, "Create symlink ok, getinodeinfo fail rc = %d",
343 rc);
1da177e4 344 } else {
b92327fe
SF
345 if (pTcon->nocase)
346 direntry->d_op = &cifs_ci_dentry_ops;
347 else
348 direntry->d_op = &cifs_dentry_ops;
1da177e4
LT
349 d_instantiate(direntry, newinode);
350 }
351 }
352
f99d49ad 353 kfree(full_path);
1da177e4
LT
354 FreeXid(xid);
355 return rc;
356}
357
cc314eef 358void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
1da177e4
LT
359{
360 char *p = nd_get_link(nd);
361 if (!IS_ERR(p))
362 kfree(p);
363}
This page took 0.407643 seconds and 5 git commands to generate.