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