link.c 14.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *   fs/cifs/link.c
 *
S
Steve French 已提交
4
 *   Copyright (C) International Business Machines  Corp., 2002,2008
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
#include <linux/fs.h>
#include <linux/stat.h>
23
#include <linux/slab.h>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30
#include <linux/namei.h>
#include "cifsfs.h"
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "cifs_fs_sb.h"
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)
#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))
#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))
#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)
#define CIFS_MF_SYMLINK_FILE_SIZE \
	(CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)

#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"
#define CIFS_MF_SYMLINK_MD5_FORMAT \
	"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n"
#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) \
	md5_hash[0],  md5_hash[1],  md5_hash[2],  md5_hash[3], \
	md5_hash[4],  md5_hash[5],  md5_hash[6],  md5_hash[7], \
	md5_hash[8],  md5_hash[9],  md5_hash[10], md5_hash[11],\
	md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15]

48 49 50 51 52 53 54 55 56
static int
symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
{
	int rc;
	unsigned int size;
	struct crypto_shash *md5;
	struct sdesc *sdescmd5;

	md5 = crypto_alloc_shash("md5", 0, 0);
57
	if (IS_ERR(md5)) {
58
		rc = PTR_ERR(md5);
59 60
		cifs_dbg(VFS, "%s: Crypto md5 allocation error %d\n",
			 __func__, rc);
61
		return rc;
62 63 64 65 66 67 68 69 70 71 72 73
	}
	size = sizeof(struct shash_desc) + crypto_shash_descsize(md5);
	sdescmd5 = kmalloc(size, GFP_KERNEL);
	if (!sdescmd5) {
		rc = -ENOMEM;
		goto symlink_hash_err;
	}
	sdescmd5->shash.tfm = md5;
	sdescmd5->shash.flags = 0x0;

	rc = crypto_shash_init(&sdescmd5->shash);
	if (rc) {
74
		cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__);
75 76
		goto symlink_hash_err;
	}
77 78
	rc = crypto_shash_update(&sdescmd5->shash, link_str, link_len);
	if (rc) {
79
		cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__);
80 81
		goto symlink_hash_err;
	}
82
	rc = crypto_shash_final(&sdescmd5->shash, md5_hash);
83
	if (rc)
84
		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
85 86 87 88 89 90 91 92

symlink_hash_err:
	crypto_free_shash(md5);
	kfree(sdescmd5);

	return rc;
}

93
static int
94 95
parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
		 char **_link_str)
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
{
	int rc;
	unsigned int link_len;
	const char *md5_str1;
	const char *link_str;
	u8 md5_hash[16];
	char md5_str2[34];

	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
		return -EINVAL;

	md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
	link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];

	rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
	if (rc != 1)
		return -EINVAL;

114 115
	rc = symlink_hash(link_len, link_str, md5_hash);
	if (rc) {
116
		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
117 118
		return rc;
	}
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135

	snprintf(md5_str2, sizeof(md5_str2),
		 CIFS_MF_SYMLINK_MD5_FORMAT,
		 CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));

	if (strncmp(md5_str1, md5_str2, 17) != 0)
		return -EINVAL;

	if (_link_str) {
		*_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
		if (!*_link_str)
			return -ENOMEM;
	}

	*_link_len = link_len;
	return 0;
}
L
Linus Torvalds 已提交
136

137
static int
138
format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
139
{
140
	int rc;
141 142 143 144 145 146 147 148 149 150 151 152
	unsigned int link_len;
	unsigned int ofs;
	u8 md5_hash[16];

	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
		return -EINVAL;

	link_len = strlen(link_str);

	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
		return -ENAMETOOLONG;

153 154
	rc = symlink_hash(link_len, link_str, md5_hash);
	if (rc) {
155
		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
156 157
		return rc;
	}
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

	snprintf(buf, buf_len,
		 CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
		 link_len,
		 CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));

	ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
	memcpy(buf + ofs, link_str, link_len);

	ofs += link_len;
	if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
		buf[ofs] = '\n';
		ofs++;
	}

	while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
		buf[ofs] = ' ';
		ofs++;
	}

	return 0;
}

181
static int
182
create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
183 184
		  struct cifs_sb_info *cifs_sb, const char *fromName,
		  const char *toName)
185 186 187 188 189 190 191 192 193
{
	int rc;
	u8 *buf;
	unsigned int bytes_written = 0;

	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

194
	rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
195 196
	if (rc)
		goto out;
197

198 199 200 201
	rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon, cifs_sb,
					fromName, buf, &bytes_written);
	if (rc)
		goto out;
202 203

	if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
204 205 206 207
		rc = -EIO;
out:
	kfree(buf);
	return rc;
208 209 210
}

static int
211
query_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
212 213
		 struct cifs_sb_info *cifs_sb, const unsigned char *path,
		 char **symlinkinfo)
214 215
{
	int rc;
216
	u8 *buf = NULL;
217
	unsigned int link_len = 0;
218
	unsigned int bytes_read = 0;
219 220 221 222 223

	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

224 225 226 227 228 229 230 231 232 233 234 235
	if (tcon->ses->server->ops->query_mf_symlink)
		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
					      cifs_sb, path, buf, &bytes_read);
	else
		rc = -ENOSYS;

	if (rc)
		goto out;

	if (bytes_read == 0) { /* not a symlink */
		rc = -EINVAL;
		goto out;
236 237
	}

238
	rc = parse_mf_symlink(buf, bytes_read, &link_len, symlinkinfo);
239
out:
240
	kfree(buf);
241
	return rc;
242 243
}

244
bool
245
couldbe_mf_symlink(const struct cifs_fattr *fattr)
246 247 248 249 250 251 252 253 254 255 256 257 258
{
	if (!(fattr->cf_mode & S_IFREG))
		/* it's not a symlink */
		return false;

	if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
		/* it's not a symlink */
		return false;

	return true;
}

int
259 260 261
cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
		      struct cifs_sb_info *cifs_sb, const unsigned char *path,
		      char *pbuf, unsigned int *pbytes_read)
262 263 264 265
{
	int rc;
	int oplock = 0;
	__u16 netfid = 0;
266
	struct cifs_io_parms io_parms;
267 268 269
	int buf_type = CIFS_NO_BUFFER;
	FILE_ALL_INFO file_info;

270
	rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, GENERIC_READ,
271 272 273 274
			 CREATE_NOT_DIR, &netfid, &oplock, &file_info,
			 cifs_sb->local_nls,
			 cifs_sb->mnt_cifs_flags &
				CIFS_MOUNT_MAP_SPECIAL_CHR);
275
	if (rc)
276
		return rc;
277

278
	if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE))
279
		/* it's not a symlink */
280
		goto out;
281

282 283
	io_parms.netfid = netfid;
	io_parms.pid = current->tgid;
284
	io_parms.tcon = tcon;
285 286
	io_parms.offset = 0;
	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
287

288
	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
289 290
out:
	CIFSSMBClose(xid, tcon, netfid);
291 292 293
	return rc;
}

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
int
cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
		       struct cifs_sb_info *cifs_sb, const unsigned char *path,
		       char *pbuf, unsigned int *pbytes_written)
{
	int rc;
	int oplock = 0;
	__u16 netfid = 0;
	struct cifs_io_parms io_parms;
	int create_options = CREATE_NOT_DIR;

	if (backup_cred(cifs_sb))
		create_options |= CREATE_OPEN_BACKUP_INTENT;

	rc = CIFSSMBOpen(xid, tcon, path, FILE_CREATE, GENERIC_WRITE,
			 create_options, &netfid, &oplock, NULL,
			 cifs_sb->local_nls,
			 cifs_sb->mnt_cifs_flags &
				CIFS_MOUNT_MAP_SPECIAL_CHR);
	if (rc)
		return rc;

	io_parms.netfid = netfid;
	io_parms.pid = current->tgid;
	io_parms.tcon = tcon;
	io_parms.offset = 0;
	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;

	rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf, NULL, 0);
	CIFSSMBClose(xid, tcon, netfid);
	return rc;
}

327
int
328 329 330
check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
		 struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
		 const unsigned char *path)
331
{
332
	int rc;
333 334 335 336
	u8 *buf = NULL;
	unsigned int link_len = 0;
	unsigned int bytes_read = 0;

337
	if (!couldbe_mf_symlink(fattr))
338 339 340 341
		/* it's not a symlink */
		return 0;

	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
342 343
	if (!buf)
		return -ENOMEM;
344

345
	if (tcon->ses->server->ops->query_mf_symlink)
346 347
		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
					      cifs_sb, path, buf, &bytes_read);
348
	else
349
		rc = -ENOSYS;
350

351
	if (rc)
352 353 354 355 356
		goto out;

	if (bytes_read == 0) /* not a symlink */
		goto out;

357
	rc = parse_mf_symlink(buf, bytes_read, &link_len, NULL);
358
	if (rc == -EINVAL) {
359
		/* it's not a symlink */
360 361 362 363
		rc = 0;
		goto out;
	}

364
	if (rc != 0)
365
		goto out;
366 367 368 369 370 371

	/* it is a symlink */
	fattr->cf_eof = link_len;
	fattr->cf_mode &= ~S_IFMT;
	fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
	fattr->cf_dtype = DT_LNK;
372
out:
373
	kfree(buf);
374
	return rc;
375 376
}

L
Linus Torvalds 已提交
377 378 379 380 381
int
cifs_hardlink(struct dentry *old_file, struct inode *inode,
	      struct dentry *direntry)
{
	int rc = -EACCES;
382
	unsigned int xid;
S
Steve French 已提交
383 384
	char *from_name = NULL;
	char *to_name = NULL;
385 386
	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
	struct tcon_link *tlink;
S
Steve French 已提交
387 388
	struct cifs_tcon *tcon;
	struct TCP_Server_Info *server;
L
Linus Torvalds 已提交
389 390
	struct cifsInodeInfo *cifsInode;

391 392 393
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink))
		return PTR_ERR(tlink);
S
Steve French 已提交
394
	tcon = tlink_tcon(tlink);
L
Linus Torvalds 已提交
395

396
	xid = get_xid();
L
Linus Torvalds 已提交
397

S
Steve French 已提交
398 399 400
	from_name = build_path_from_dentry(old_file);
	to_name = build_path_from_dentry(direntry);
	if ((from_name == NULL) || (to_name == NULL)) {
L
Linus Torvalds 已提交
401 402 403 404
		rc = -ENOMEM;
		goto cifs_hl_exit;
	}

S
Steve French 已提交
405 406
	if (tcon->unix_ext)
		rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,
407 408
					    cifs_sb->local_nls,
					    cifs_sb->mnt_cifs_flags &
409
						CIFS_MOUNT_MAP_SPECIAL_CHR);
L
Linus Torvalds 已提交
410
	else {
S
Steve French 已提交
411
		server = tcon->ses->server;
412 413 414 415
		if (!server->ops->create_hardlink) {
			rc = -ENOSYS;
			goto cifs_hl_exit;
		}
S
Steve French 已提交
416 417
		rc = server->ops->create_hardlink(xid, tcon, from_name, to_name,
						  cifs_sb);
S
Steve French 已提交
418 419
		if ((rc == -EIO) || (rc == -EINVAL))
			rc = -EOPNOTSUPP;
L
Linus Torvalds 已提交
420 421
	}

422 423
	d_drop(direntry);	/* force new lookup from server of target */

S
Steve French 已提交
424 425 426 427
	/*
	 * if source file is cached (oplocked) revalidate will not go to server
	 * until the file is closed or oplock broken so update nlinks locally
	 */
S
Steve French 已提交
428
	if (old_file->d_inode) {
429
		cifsInode = CIFS_I(old_file->d_inode);
S
Steve French 已提交
430
		if (rc == 0) {
431
			spin_lock(&old_file->d_inode->i_lock);
432
			inc_nlink(old_file->d_inode);
433
			spin_unlock(&old_file->d_inode->i_lock);
S
Steve French 已提交
434 435 436 437 438 439 440 441 442 443 444
			/*
			 * BB should we make this contingent on superblock flag
			 * NOATIME?
			 */
			/* 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?
			 */
445
		}
S
Steve French 已提交
446 447 448 449
		/*
		 * if not oplocked will force revalidate to get info on source
		 * file from srv
		 */
450 451
		cifsInode->time = 0;

S
Steve French 已提交
452 453 454 455 456 457 458 459
		/*
		 * 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;
		 */
L
Linus Torvalds 已提交
460 461 462
	}

cifs_hl_exit:
S
Steve French 已提交
463 464
	kfree(from_name);
	kfree(to_name);
465
	free_xid(xid);
466
	cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
467 468 469
	return rc;
}

470
void *
L
Linus Torvalds 已提交
471 472 473
cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
{
	struct inode *inode = direntry->d_inode;
474
	int rc = -ENOMEM;
475
	unsigned int xid;
L
Linus Torvalds 已提交
476
	char *full_path = NULL;
477 478
	char *target_path = NULL;
	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
479
	struct tcon_link *tlink = NULL;
480
	struct cifs_tcon *tcon;
481
	struct TCP_Server_Info *server;
L
Linus Torvalds 已提交
482

483
	xid = get_xid();
L
Linus Torvalds 已提交
484

485 486 487 488 489 490 491
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink)) {
		rc = PTR_ERR(tlink);
		tlink = NULL;
		goto out;
	}
	tcon = tlink_tcon(tlink);
492
	server = tcon->ses->server;
L
Linus Torvalds 已提交
493

494
	full_path = build_path_from_dentry(direntry);
L
Linus Torvalds 已提交
495
	if (!full_path)
496
		goto out;
L
Linus Torvalds 已提交
497

498
	cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);
L
Linus Torvalds 已提交
499

500 501 502 503 504 505
	rc = -EACCES;
	/*
	 * First try Minshall+French Symlinks, if configured
	 * and fallback to UNIX Extensions Symlinks.
	 */
	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
506 507
		rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
				      &target_path);
508

509
	if ((rc != 0) && cap_unix(tcon->ses))
510 511
		rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
					     cifs_sb->local_nls);
512 513 514
	else if (rc != 0 && server->ops->query_symlink)
		rc = server->ops->query_symlink(xid, tcon, full_path,
						&target_path, cifs_sb);
515

516 517
	kfree(full_path);
out:
518
	if (rc != 0) {
L
Linus Torvalds 已提交
519 520 521 522
		kfree(target_path);
		target_path = ERR_PTR(rc);
	}

523
	free_xid(xid);
524 525
	if (tlink)
		cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
526
	nd_set_link(nd, target_path);
527
	return NULL;
L
Linus Torvalds 已提交
528 529 530 531 532 533
}

int
cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
{
	int rc = -EOPNOTSUPP;
534
	unsigned int xid;
535 536
	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
	struct tcon_link *tlink;
537
	struct cifs_tcon *pTcon;
L
Linus Torvalds 已提交
538 539 540
	char *full_path = NULL;
	struct inode *newinode = NULL;

541
	xid = get_xid();
L
Linus Torvalds 已提交
542

543 544 545 546 547 548
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink)) {
		rc = PTR_ERR(tlink);
		goto symlink_exit;
	}
	pTcon = tlink_tcon(tlink);
L
Linus Torvalds 已提交
549

550
	full_path = build_path_from_dentry(direntry);
S
Steve French 已提交
551
	if (full_path == NULL) {
552
		rc = -ENOMEM;
553
		goto symlink_exit;
L
Linus Torvalds 已提交
554 555
	}

556 557
	cifs_dbg(FYI, "Full path: %s\n", full_path);
	cifs_dbg(FYI, "symname is %s\n", symname);
L
Linus Torvalds 已提交
558 559

	/* BB what if DFS and this volume is on different share? BB */
560
	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
561
		rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
562
	else if (pTcon->unix_ext)
L
Linus Torvalds 已提交
563 564 565
		rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
					   cifs_sb->local_nls);
	/* else
S
Steve French 已提交
566 567
	   rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
					cifs_sb_target->local_nls); */
L
Linus Torvalds 已提交
568 569

	if (rc == 0) {
570
		if (pTcon->unix_ext)
L
Linus Torvalds 已提交
571
			rc = cifs_get_inode_info_unix(&newinode, full_path,
S
Steve French 已提交
572
						      inode->i_sb, xid);
L
Linus Torvalds 已提交
573 574
		else
			rc = cifs_get_inode_info(&newinode, full_path, NULL,
575
						 inode->i_sb, xid, NULL);
L
Linus Torvalds 已提交
576 577

		if (rc != 0) {
578 579
			cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",
				 rc);
L
Linus Torvalds 已提交
580 581 582 583
		} else {
			d_instantiate(direntry, newinode);
		}
	}
584
symlink_exit:
J
Jesper Juhl 已提交
585
	kfree(full_path);
586
	cifs_put_tlink(tlink);
587
	free_xid(xid);
L
Linus Torvalds 已提交
588 589
	return rc;
}