X-Git-Url: https://git.kernel.dk/?a=blobdiff_plain;f=fs%2Fcifs%2Flink.c;h=85cdbf831e7baca6c3d3fbdb1ac670e69fc595ad;hb=5398a64c63a69a0ac33dbae458ea4aab0dc23f14;hp=6e4e8957595d19a124ac1dbab468e407cbf49737;hpb=0fd43ae4758b2841656afda4439b80e8a3603af2;p=linux-2.6-block.git diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 6e4e8957595d..85cdbf831e7b 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -91,6 +91,92 @@ CIFSParseMFSymlink(const u8 *buf, return 0; } +static int +CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) +{ + unsigned int link_len; + unsigned int ofs; + struct MD5Context md5_ctx; + 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; + + cifs_MD5_init(&md5_ctx); + cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len); + cifs_MD5_final(md5_hash, &md5_ctx); + + 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; +} + +static int +CIFSCreateMFSymLink(const int xid, struct cifsTconInfo *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap) +{ + int rc; + int oplock = 0; + __u16 netfid = 0; + u8 *buf; + unsigned int bytes_written = 0; + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); + if (rc != 0) { + kfree(buf); + return rc; + } + + rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, + CREATE_NOT_DIR, &netfid, &oplock, NULL, + nls_codepage, remap); + if (rc != 0) { + kfree(buf); + return rc; + } + + rc = CIFSSMBWrite(xid, tcon, netfid, + CIFS_MF_SYMLINK_FILE_SIZE /* length */, + 0 /* offset */, + &bytes_written, buf, NULL, 0); + CIFSSMBClose(xid, tcon, netfid); + kfree(buf); + if (rc != 0) + return rc; + + if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) + return -EIO; + + return 0; +} + static int CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char **symlinkinfo, @@ -163,7 +249,8 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, int rc; int oplock = 0; __u16 netfid = 0; - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct tcon_link *tlink; + struct cifsTconInfo *pTcon; u8 *buf; char *pbuf; unsigned int bytes_read = 0; @@ -175,23 +262,30 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, /* it's not a symlink */ return 0; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, CREATE_NOT_DIR, &netfid, &oplock, &file_info, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc != 0) - return rc; + goto out; if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { CIFSSMBClose(xid, pTcon, netfid); /* it's not a symlink */ - return 0; + goto out; } buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (!buf) { + rc = -ENOMEM; + goto out; + } pbuf = buf; rc = CIFSSMBRead(xid, pTcon, netfid, @@ -201,23 +295,28 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, CIFSSMBClose(xid, pTcon, netfid); if (rc != 0) { kfree(buf); - return rc; + goto out; } rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); kfree(buf); - if (rc == -EINVAL) + if (rc == -EINVAL) { /* it's not a symlink */ - return 0; + rc = 0; + goto out; + } + if (rc != 0) - return rc; + goto out; /* 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; - return 0; +out: + cifs_put_tlink(tlink); + return rc; } int @@ -228,17 +327,17 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, int xid; char *fromName = NULL; char *toName = NULL; - struct cifs_sb_info *cifs_sb_target; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct cifsInodeInfo *cifsInode; - xid = GetXid(); - - cifs_sb_target = CIFS_SB(inode->i_sb); - pTcon = cifs_sb_target->tcon; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); -/* No need to check for cross device links since server will do that - BB note DFS case in future though (when we may have to check) */ + xid = GetXid(); fromName = build_path_from_dentry(old_file); toName = build_path_from_dentry(direntry); @@ -247,16 +346,15 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, goto cifs_hl_exit; } -/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/ if (pTcon->unix_ext) rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, - cifs_sb_target->local_nls, - cifs_sb_target->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else { rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, - cifs_sb_target->local_nls, - cifs_sb_target->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if ((rc == -EIO) || (rc == -EINVAL)) rc = -EOPNOTSUPP; @@ -292,6 +390,7 @@ cifs_hl_exit: kfree(fromName); kfree(toName); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -304,10 +403,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) char *full_path = NULL; char *target_path = NULL; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; + struct tcon_link *tlink = NULL; + struct cifsTconInfo *tcon; xid = GetXid(); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + tlink = NULL; + goto out; + } + tcon = tlink_tcon(tlink); + /* * For now, we just handle symlinks with unix extensions enabled. * Eventually we should handle NTFS reparse points, and MacOS @@ -321,7 +429,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) * but there doesn't seem to be any harm in allowing the client to * read them. */ - if (!(tcon->ses->capabilities & CAP_UNIX)) { + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + && !(tcon->ses->capabilities & CAP_UNIX)) { rc = -EACCES; goto out; } @@ -332,8 +441,21 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); - rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, - cifs_sb->local_nls); + 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) + rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + + if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX)) + rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, + cifs_sb->local_nls); + kfree(full_path); out: if (rc != 0) { @@ -342,6 +464,8 @@ out: } FreeXid(xid); + if (tlink) + cifs_put_tlink(tlink); nd_set_link(nd, target_path); return NULL; } @@ -351,29 +475,37 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) { int rc = -EOPNOTSUPP; int xid; - struct cifs_sb_info *cifs_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; xid = GetXid(); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto symlink_exit; + } + pTcon = tlink_tcon(tlink); full_path = build_path_from_dentry(direntry); - if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto symlink_exit; } cFYI(1, "Full path: %s", full_path); cFYI(1, "symname is %s", symname); /* BB what if DFS and this volume is on different share? BB */ - if (pTcon->unix_ext) + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + else if (pTcon->unix_ext) rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, cifs_sb->local_nls); /* else @@ -399,8 +531,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) d_instantiate(direntry, newinode); } } - +symlink_exit: kfree(full_path); + cifs_put_tlink(tlink); FreeXid(xid); return rc; }