File Index
Files and directories
Sign in
FILE

link.c

File preview with syntax highlighting.

linux-6.19.14/fs/smb/client/link.c C
  1// SPDX-License-Identifier: LGPL-2.1
  2/*
  3 *
  4 *   Copyright (C) International Business Machines  Corp., 2002,2008
  5 *   Author(s): Steve French (sfrench@us.ibm.com)
  6 *
  7 */
  8#include <crypto/md5.h>
  9#include <linux/fs.h>
 10#include <linux/stat.h>
 11#include <linux/slab.h>
 12#include <linux/namei.h>
 13#include "cifsfs.h"
 14#include "cifspdu.h"
 15#include "cifsglob.h"
 16#include "cifsproto.h"
 17#include "cifs_debug.h"
 18#include "cifs_fs_sb.h"
 19#include "cifs_unicode.h"
 20#include "smb2proto.h"
 21#include "cifs_ioctl.h"
 22#include "fs_context.h"
 23#include "reparse.h"
 24
 25/*
 26 * M-F Symlink Functions - Begin
 27 */
 28
 29#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)
 30#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))
 31#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))
 32#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)
 33#define CIFS_MF_SYMLINK_FILE_SIZE \
 34	(CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)
 35
 36#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"
 37#define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n"
 38#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash
 39
 40static int
 41parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
 42		 char **_link_str)
 43{
 44	int rc;
 45	unsigned int link_len;
 46	const char *md5_str1;
 47	const char *link_str;
 48	u8 md5_hash[16];
 49	char md5_str2[34];
 50
 51	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
 52		return -EINVAL;
 53
 54	md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
 55	link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];
 56
 57	rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
 58	if (rc != 1)
 59		return -EINVAL;
 60
 61	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
 62		return -EINVAL;
 63
 64	md5(link_str, link_len, md5_hash);
 65
 66	scnprintf(md5_str2, sizeof(md5_str2),
 67		  CIFS_MF_SYMLINK_MD5_FORMAT,
 68		  CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
 69
 70	if (strncmp(md5_str1, md5_str2, 17) != 0)
 71		return -EINVAL;
 72
 73	if (_link_str) {
 74		*_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
 75		if (!*_link_str)
 76			return -ENOMEM;
 77	}
 78
 79	*_link_len = link_len;
 80	return 0;
 81}
 82
 83static int
 84format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
 85{
 86	unsigned int link_len;
 87	unsigned int ofs;
 88	u8 md5_hash[16];
 89
 90	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
 91		return -EINVAL;
 92
 93	link_len = strlen(link_str);
 94
 95	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
 96		return -ENAMETOOLONG;
 97
 98	md5(link_str, link_len, md5_hash);
 99
100	scnprintf(buf, buf_len,
101		  CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
102		  link_len,
103		  CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
104
105	ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
106	memcpy(buf + ofs, link_str, link_len);
107
108	ofs += link_len;
109	if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
110		buf[ofs] = '\n';
111		ofs++;
112	}
113
114	while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
115		buf[ofs] = ' ';
116		ofs++;
117	}
118
119	return 0;
120}
121
122bool
123couldbe_mf_symlink(const struct cifs_fattr *fattr)
124{
125	if (!S_ISREG(fattr->cf_mode))
126		/* it's not a symlink */
127		return false;
128
129	if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
130		/* it's not a symlink */
131		return false;
132
133	return true;
134}
135
136static int
137create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
138		  struct cifs_sb_info *cifs_sb, const char *fromName,
139		  const char *toName)
140{
141	int rc;
142	u8 *buf;
143	unsigned int bytes_written = 0;
144
145	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
146	if (!buf)
147		return -ENOMEM;
148
149	rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
150	if (rc)
151		goto out;
152
153	if (tcon->ses->server->ops->create_mf_symlink)
154		rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon,
155					cifs_sb, fromName, buf, &bytes_written);
156	else
157		rc = -EOPNOTSUPP;
158
159	if (rc)
160		goto out;
161
162	if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
163		rc = smb_EIO2(smb_eio_trace_symlink_file_size,
164			      bytes_written, CIFS_MF_SYMLINK_FILE_SIZE);
165out:
166	kfree(buf);
167	return rc;
168}
169
170int
171check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
172		 struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
173		 const unsigned char *path)
174{
175	int rc;
176	u8 *buf = NULL;
177	unsigned int link_len = 0;
178	unsigned int bytes_read = 0;
179	char *symlink = NULL;
180
181	if (!couldbe_mf_symlink(fattr))
182		/* it's not a symlink */
183		return 0;
184
185	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
186	if (!buf)
187		return -ENOMEM;
188
189	if (tcon->ses->server->ops->query_mf_symlink)
190		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
191					      cifs_sb, path, buf, &bytes_read);
192	else
193		rc = -ENOSYS;
194
195	if (rc)
196		goto out;
197
198	if (bytes_read == 0) /* not a symlink */
199		goto out;
200
201	rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink);
202	if (rc == -EINVAL) {
203		/* it's not a symlink */
204		rc = 0;
205		goto out;
206	}
207
208	if (rc != 0)
209		goto out;
210
211	/* it is a symlink */
212	fattr->cf_eof = link_len;
213	fattr->cf_mode &= ~S_IFMT;
214	fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
215	fattr->cf_dtype = DT_LNK;
216	fattr->cf_symlink_target = symlink;
217out:
218	kfree(buf);
219	return rc;
220}
221
222#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
223/*
224 * SMB 1.0 Protocol specific functions
225 */
226
227int
228cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
229		      struct cifs_sb_info *cifs_sb, const unsigned char *path,
230		      char *pbuf, unsigned int *pbytes_read)
231{
232	int rc;
233	int oplock = 0;
234	struct cifs_fid fid;
235	struct cifs_open_parms oparms;
236	struct cifs_io_parms io_parms = {0};
237	int buf_type = CIFS_NO_BUFFER;
238	struct cifs_open_info_data query_data;
239
240	oparms = (struct cifs_open_parms) {
241		.tcon = tcon,
242		.cifs_sb = cifs_sb,
243		.desired_access = GENERIC_READ,
244		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
245		.disposition = FILE_OPEN,
246		.path = path,
247		.fid = &fid,
248	};
249
250	rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &query_data);
251	if (rc)
252		return rc;
253
254	if (query_data.fi.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
255		rc = -ENOENT;
256		/* it's not a symlink */
257		goto out;
258	}
259
260	io_parms.netfid = fid.netfid;
261	io_parms.pid = current->tgid;
262	io_parms.tcon = tcon;
263	io_parms.offset = 0;
264	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
265
266	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
267out:
268	CIFSSMBClose(xid, tcon, fid.netfid);
269	return rc;
270}
271
272int
273cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
274		       struct cifs_sb_info *cifs_sb, const unsigned char *path,
275		       char *pbuf, unsigned int *pbytes_written)
276{
277	int rc;
278	int oplock = 0;
279	struct cifs_fid fid;
280	struct cifs_open_parms oparms;
281	struct cifs_io_parms io_parms = {0};
282
283	oparms = (struct cifs_open_parms) {
284		.tcon = tcon,
285		.cifs_sb = cifs_sb,
286		.desired_access = GENERIC_WRITE,
287		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
288		.disposition = FILE_CREATE,
289		.path = path,
290		.fid = &fid,
291	};
292
293	rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
294	if (rc)
295		return rc;
296
297	io_parms.netfid = fid.netfid;
298	io_parms.pid = current->tgid;
299	io_parms.tcon = tcon;
300	io_parms.offset = 0;
301	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
302
303	rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf);
304	CIFSSMBClose(xid, tcon, fid.netfid);
305	return rc;
306}
307#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
308
309/*
310 * SMB 2.1/SMB3 Protocol specific functions
311 */
312int
313smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
314		      struct cifs_sb_info *cifs_sb, const unsigned char *path,
315		      char *pbuf, unsigned int *pbytes_read)
316{
317	int rc;
318	struct cifs_fid fid;
319	struct cifs_open_parms oparms;
320	struct cifs_io_parms io_parms = {0};
321	int buf_type = CIFS_NO_BUFFER;
322	__le16 *utf16_path;
323	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
324	struct smb2_file_all_info *pfile_info = NULL;
325
326	oparms = (struct cifs_open_parms) {
327		.tcon = tcon,
328		.cifs_sb = cifs_sb,
329		.path = path,
330		.desired_access = GENERIC_READ,
331		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
332		.disposition = FILE_OPEN,
333		.fid = &fid,
334	};
335
336	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
337	if (utf16_path == NULL)
338		return -ENOMEM;
339
340	pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
341			     GFP_KERNEL);
342
343	if (pfile_info == NULL) {
344		kfree(utf16_path);
345		return  -ENOMEM;
346	}
347
348	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
349		       NULL, NULL);
350	if (rc)
351		goto qmf_out_open_fail;
352
353	if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
354		/* it's not a symlink */
355		rc = -ENOENT; /* Is there a better rc to return? */
356		goto qmf_out;
357	}
358
359	io_parms.netfid = fid.netfid;
360	io_parms.pid = current->tgid;
361	io_parms.tcon = tcon;
362	io_parms.offset = 0;
363	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
364	io_parms.persistent_fid = fid.persistent_fid;
365	io_parms.volatile_fid = fid.volatile_fid;
366	rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
367qmf_out:
368	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
369qmf_out_open_fail:
370	kfree(utf16_path);
371	kfree(pfile_info);
372	return rc;
373}
374
375int
376smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
377		       struct cifs_sb_info *cifs_sb, const unsigned char *path,
378		       char *pbuf, unsigned int *pbytes_written)
379{
380	int rc;
381	struct cifs_fid fid;
382	struct cifs_open_parms oparms;
383	struct cifs_io_parms io_parms = {0};
384	__le16 *utf16_path;
385	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
386	struct kvec iov[2];
387
388	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
389
390	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
391	if (!utf16_path)
392		return -ENOMEM;
393
394	oparms = (struct cifs_open_parms) {
395		.tcon = tcon,
396		.cifs_sb = cifs_sb,
397		.path = path,
398		.desired_access = GENERIC_WRITE,
399		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
400		.disposition = FILE_CREATE,
401		.fid = &fid,
402		.mode = 0644,
403	};
404
405	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
406		       NULL, NULL);
407	if (rc) {
408		kfree(utf16_path);
409		return rc;
410	}
411
412	io_parms.netfid = fid.netfid;
413	io_parms.pid = current->tgid;
414	io_parms.tcon = tcon;
415	io_parms.offset = 0;
416	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
417	io_parms.persistent_fid = fid.persistent_fid;
418	io_parms.volatile_fid = fid.volatile_fid;
419
420	/* iov[0] is reserved for smb header */
421	iov[1].iov_base = pbuf;
422	iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE;
423
424	rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1);
425
426	/* Make sure we wrote all of the symlink data */
427	if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE))
428		rc = smb_EIO2(smb_eio_trace_short_symlink_write,
429			      *pbytes_written, CIFS_MF_SYMLINK_FILE_SIZE);
430
431	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
432
433	kfree(utf16_path);
434	return rc;
435}
436
437/*
438 * M-F Symlink Functions - End
439 */
440
441int
442cifs_hardlink(struct dentry *old_file, struct inode *inode,
443	      struct dentry *direntry)
444{
445	int rc = -EACCES;
446	unsigned int xid;
447	const char *from_name, *to_name;
448	void *page1, *page2;
449	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
450	struct tcon_link *tlink;
451	struct cifs_tcon *tcon;
452	struct TCP_Server_Info *server;
453	struct cifsInodeInfo *cifsInode;
454
455	if (unlikely(cifs_forced_shutdown(cifs_sb)))
456		return smb_EIO(smb_eio_trace_forced_shutdown);
457
458	tlink = cifs_sb_tlink(cifs_sb);
459	if (IS_ERR(tlink))
460		return PTR_ERR(tlink);
461	tcon = tlink_tcon(tlink);
462
463	xid = get_xid();
464	page1 = alloc_dentry_path();
465	page2 = alloc_dentry_path();
466
467	from_name = build_path_from_dentry(old_file, page1);
468	if (IS_ERR(from_name)) {
469		rc = PTR_ERR(from_name);
470		goto cifs_hl_exit;
471	}
472	to_name = build_path_from_dentry(direntry, page2);
473	if (IS_ERR(to_name)) {
474		rc = PTR_ERR(to_name);
475		goto cifs_hl_exit;
476	}
477
478#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
479	if (tcon->unix_ext)
480		rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,
481					    cifs_sb->local_nls,
482					    cifs_remap(cifs_sb));
483	else {
484#else
485	{
486#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
487		server = tcon->ses->server;
488		if (!server->ops->create_hardlink) {
489			rc = -ENOSYS;
490			goto cifs_hl_exit;
491		}
492		rc = server->ops->create_hardlink(xid, tcon, old_file,
493						  from_name, to_name, cifs_sb);
494		if ((rc == -EIO) || (rc == -EINVAL))
495			rc = -EOPNOTSUPP;
496	}
497
498	d_drop(direntry);	/* force new lookup from server of target */
499
500	/*
501	 * if source file is cached (oplocked) revalidate will not go to server
502	 * until the file is closed or oplock broken so update nlinks locally
503	 */
504	if (d_really_is_positive(old_file)) {
505		cifsInode = CIFS_I(d_inode(old_file));
506		if (rc == 0) {
507			spin_lock(&d_inode(old_file)->i_lock);
508			inc_nlink(d_inode(old_file));
509			spin_unlock(&d_inode(old_file)->i_lock);
510
511			/*
512			 * parent dir timestamps will update from srv within a
513			 * second, would it really be worth it to set the parent
514			 * dir cifs inode time to zero to force revalidate
515			 * (faster) for it too?
516			 */
517		}
518		/*
519		 * if not oplocked will force revalidate to get info on source
520		 * file from srv.  Note Samba server prior to 4.2 has bug -
521		 * not updating src file ctime on hardlinks but Windows servers
522		 * handle it properly
523		 */
524		cifsInode->time = 0;
525
526		/*
527		 * Will update parent dir timestamps from srv within a second.
528		 * Would it really be worth it to set the parent dir (cifs
529		 * inode) time field to zero to force revalidate on parent
530		 * directory faster ie
531		 *
532		 * CIFS_I(inode)->time = 0;
533		 */
534	}
535
536cifs_hl_exit:
537	free_dentry_path(page1);
538	free_dentry_path(page2);
539	free_xid(xid);
540	cifs_put_tlink(tlink);
541	return rc;
542}
543
544int
545cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
546	     struct dentry *direntry, const char *symname)
547{
548	int rc = -EOPNOTSUPP;
549	unsigned int xid;
550	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
551	struct tcon_link *tlink;
552	struct cifs_tcon *pTcon;
553	const char *full_path;
554	void *page;
555	struct inode *newinode = NULL;
556
557	if (unlikely(cifs_forced_shutdown(cifs_sb)))
558		return smb_EIO(smb_eio_trace_forced_shutdown);
559
560	page = alloc_dentry_path();
561	if (!page)
562		return -ENOMEM;
563
564	xid = get_xid();
565
566	tlink = cifs_sb_tlink(cifs_sb);
567	if (IS_ERR(tlink)) {
568		rc = PTR_ERR(tlink);
569		/* BB could be clearer if skipped put_tlink on error here, but harmless */
570		goto symlink_exit;
571	}
572	pTcon = tlink_tcon(tlink);
573
574	full_path = build_path_from_dentry(direntry, page);
575	if (IS_ERR(full_path)) {
576		rc = PTR_ERR(full_path);
577		goto symlink_exit;
578	}
579
580	cifs_dbg(FYI, "Full path: %s\n", full_path);
581	cifs_dbg(FYI, "symname is %s\n", symname);
582
583	/* BB what if DFS and this volume is on different share? BB */
584	rc = -EOPNOTSUPP;
585	switch (cifs_symlink_type(cifs_sb)) {
586	case CIFS_SYMLINK_TYPE_UNIX:
587#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
588		if (pTcon->unix_ext) {
589			rc = CIFSUnixCreateSymLink(xid, pTcon, full_path,
590						   symname,
591						   cifs_sb->local_nls,
592						   cifs_remap(cifs_sb));
593		}
594#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
595		break;
596
597	case CIFS_SYMLINK_TYPE_MFSYMLINKS:
598		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
599			rc = create_mf_symlink(xid, pTcon, cifs_sb,
600					       full_path, symname);
601		}
602		break;
603
604	case CIFS_SYMLINK_TYPE_SFU:
605		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
606			rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
607						  full_path, S_IFLNK,
608						  0, symname);
609		}
610		break;
611
612	case CIFS_SYMLINK_TYPE_NATIVE:
613	case CIFS_SYMLINK_TYPE_NFS:
614	case CIFS_SYMLINK_TYPE_WSL:
615		if (CIFS_REPARSE_SUPPORT(pTcon)) {
616			rc = create_reparse_symlink(xid, inode, direntry, pTcon,
617						    full_path, symname);
618			goto symlink_exit;
619		}
620		break;
621	default:
622		break;
623	}
624
625	if (rc == 0) {
626		if (pTcon->posix_extensions) {
627			rc = smb311_posix_get_inode_info(&newinode, full_path,
628							 NULL, inode->i_sb, xid);
629		} else if (pTcon->unix_ext) {
630			rc = cifs_get_inode_info_unix(&newinode, full_path,
631						      inode->i_sb, xid);
632		} else {
633			rc = cifs_get_inode_info(&newinode, full_path, NULL,
634						 inode->i_sb, xid, NULL);
635		}
636
637		if (rc != 0) {
638			cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",
639				 rc);
640		} else {
641			d_instantiate(direntry, newinode);
642		}
643	}
644symlink_exit:
645	free_dentry_path(page);
646	cifs_put_tlink(tlink);
647	free_xid(xid);
648	return rc;
649}
~/ Read-only file preview.