diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-22 20:52:29 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-22 20:52:29 -0400 |
| commit | d2ecad9faca2221ae6f664f146f0dcae661bf39d (patch) | |
| tree | 9a13aabd0925e41586a292d34990ac2df73c34b2 /fs/cifs/link.c | |
| parent | c70b5296e775cde46cfcb2d860ba160108a5ec7a (diff) | |
| parent | cdff08e76612e53580139653403aedea979aa639 (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6: (56 commits)
[CIFS] move close processing from cifs_close to cifsFileInfo_put
cifs: convert cifs_tcp_ses_lock from a rwlock to a spinlock
cifs: cancel_delayed_work() + flush_scheduled_work() -> cancel_delayed_work_sync()
Clean up two declarations of blob_len
cifs: move cifsFileInfo_put to file.c
cifs: convert GlobalSMBSeslock from a rwlock to regular spinlock
[CIFS] Fix minor checkpatch warning and update cifs version
cifs: move cifs_new_fileinfo to file.c
cifs: eliminate pfile pointer from cifsFileInfo
cifs: cifs_write argument change and cleanup
cifs: clean up cifs_reopen_file
cifs: eliminate the inode argument from cifs_new_fileinfo
cifs: eliminate oflags option from cifs_new_fileinfo
cifs: fix flags handling in cifs_posix_open
cifs: eliminate cifs_posix_open_inode_helper
cifs: handle FindFirst failure gracefully
NTLM authentication and signing - Calculate auth response per smb session
cifs: don't use vfsmount to pin superblock for oplock breaks
cifs: keep dentry reference in cifsFileInfo instead of inode reference
cifs: on multiuser mount, set ownership to current_fsuid/current_fsgid (try #7)
...
Fix up trivial conflict in fs/cifs/cifsfs.c due to added/removed header files
Diffstat (limited to 'fs/cifs/link.c')
| -rw-r--r-- | fs/cifs/link.c | 372 |
1 files changed, 348 insertions, 24 deletions
diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 473ca8033656..85cdbf831e7b 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c | |||
| @@ -28,6 +28,296 @@ | |||
| 28 | #include "cifsproto.h" | 28 | #include "cifsproto.h" |
| 29 | #include "cifs_debug.h" | 29 | #include "cifs_debug.h" |
| 30 | #include "cifs_fs_sb.h" | 30 | #include "cifs_fs_sb.h" |
| 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 | |||
| 49 | static int | ||
| 50 | CIFSParseMFSymlink(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 | } | ||
| 93 | |||
| 94 | static int | ||
| 95 | CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) | ||
| 96 | { | ||
| 97 | unsigned int link_len; | ||
| 98 | unsigned int ofs; | ||
| 99 | struct MD5Context md5_ctx; | ||
| 100 | u8 md5_hash[16]; | ||
| 101 | |||
| 102 | if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) | ||
| 103 | return -EINVAL; | ||
| 104 | |||
| 105 | link_len = strlen(link_str); | ||
| 106 | |||
| 107 | if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) | ||
| 108 | return -ENAMETOOLONG; | ||
| 109 | |||
| 110 | cifs_MD5_init(&md5_ctx); | ||
| 111 | cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len); | ||
| 112 | cifs_MD5_final(md5_hash, &md5_ctx); | ||
| 113 | |||
| 114 | snprintf(buf, buf_len, | ||
| 115 | CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, | ||
| 116 | link_len, | ||
| 117 | CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); | ||
| 118 | |||
| 119 | ofs = CIFS_MF_SYMLINK_LINK_OFFSET; | ||
| 120 | memcpy(buf + ofs, link_str, link_len); | ||
| 121 | |||
| 122 | ofs += link_len; | ||
| 123 | if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { | ||
| 124 | buf[ofs] = '\n'; | ||
| 125 | ofs++; | ||
| 126 | } | ||
| 127 | |||
| 128 | while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { | ||
| 129 | buf[ofs] = ' '; | ||
| 130 | ofs++; | ||
| 131 | } | ||
| 132 | |||
| 133 | return 0; | ||
| 134 | } | ||
| 135 | |||
| 136 | static int | ||
| 137 | CIFSCreateMFSymLink(const int xid, struct cifsTconInfo *tcon, | ||
| 138 | const char *fromName, const char *toName, | ||
| 139 | const struct nls_table *nls_codepage, int remap) | ||
| 140 | { | ||
| 141 | int rc; | ||
| 142 | int oplock = 0; | ||
| 143 | __u16 netfid = 0; | ||
| 144 | u8 *buf; | ||
| 145 | unsigned int bytes_written = 0; | ||
| 146 | |||
| 147 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
| 148 | if (!buf) | ||
| 149 | return -ENOMEM; | ||
| 150 | |||
| 151 | rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); | ||
| 152 | if (rc != 0) { | ||
| 153 | kfree(buf); | ||
| 154 | return rc; | ||
| 155 | } | ||
| 156 | |||
| 157 | rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, | ||
| 158 | CREATE_NOT_DIR, &netfid, &oplock, NULL, | ||
| 159 | nls_codepage, remap); | ||
| 160 | if (rc != 0) { | ||
| 161 | kfree(buf); | ||
| 162 | return rc; | ||
| 163 | } | ||
| 164 | |||
| 165 | rc = CIFSSMBWrite(xid, tcon, netfid, | ||
| 166 | CIFS_MF_SYMLINK_FILE_SIZE /* length */, | ||
| 167 | 0 /* offset */, | ||
| 168 | &bytes_written, buf, NULL, 0); | ||
| 169 | CIFSSMBClose(xid, tcon, netfid); | ||
| 170 | kfree(buf); | ||
| 171 | if (rc != 0) | ||
| 172 | return rc; | ||
| 173 | |||
| 174 | if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) | ||
| 175 | return -EIO; | ||
| 176 | |||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | static int | ||
| 181 | CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon, | ||
| 182 | const unsigned char *searchName, char **symlinkinfo, | ||
| 183 | const struct nls_table *nls_codepage, int remap) | ||
| 184 | { | ||
| 185 | int rc; | ||
| 186 | int oplock = 0; | ||
| 187 | __u16 netfid = 0; | ||
| 188 | u8 *buf; | ||
| 189 | char *pbuf; | ||
| 190 | unsigned int bytes_read = 0; | ||
| 191 | int buf_type = CIFS_NO_BUFFER; | ||
| 192 | unsigned int link_len = 0; | ||
| 193 | FILE_ALL_INFO file_info; | ||
| 194 | |||
| 195 | rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ, | ||
| 196 | CREATE_NOT_DIR, &netfid, &oplock, &file_info, | ||
| 197 | nls_codepage, remap); | ||
| 198 | if (rc != 0) | ||
| 199 | return rc; | ||
| 200 | |||
| 201 | if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { | ||
| 202 | CIFSSMBClose(xid, tcon, netfid); | ||
| 203 | /* it's not a symlink */ | ||
| 204 | return -EINVAL; | ||
| 205 | } | ||
| 206 | |||
| 207 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
| 208 | if (!buf) | ||
| 209 | return -ENOMEM; | ||
| 210 | pbuf = buf; | ||
| 211 | |||
| 212 | rc = CIFSSMBRead(xid, tcon, netfid, | ||
| 213 | CIFS_MF_SYMLINK_FILE_SIZE /* length */, | ||
| 214 | 0 /* offset */, | ||
| 215 | &bytes_read, &pbuf, &buf_type); | ||
| 216 | CIFSSMBClose(xid, tcon, netfid); | ||
| 217 | if (rc != 0) { | ||
| 218 | kfree(buf); | ||
| 219 | return rc; | ||
| 220 | } | ||
| 221 | |||
| 222 | rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo); | ||
| 223 | kfree(buf); | ||
| 224 | if (rc != 0) | ||
| 225 | return rc; | ||
| 226 | |||
| 227 | return 0; | ||
| 228 | } | ||
| 229 | |||
| 230 | bool | ||
| 231 | CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr) | ||
| 232 | { | ||
| 233 | if (!(fattr->cf_mode & S_IFREG)) | ||
| 234 | /* it's not a symlink */ | ||
| 235 | return false; | ||
| 236 | |||
| 237 | if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) | ||
| 238 | /* it's not a symlink */ | ||
| 239 | return false; | ||
| 240 | |||
| 241 | return true; | ||
| 242 | } | ||
| 243 | |||
| 244 | int | ||
| 245 | CIFSCheckMFSymlink(struct cifs_fattr *fattr, | ||
| 246 | const unsigned char *path, | ||
| 247 | struct cifs_sb_info *cifs_sb, int xid) | ||
| 248 | { | ||
| 249 | int rc; | ||
| 250 | int oplock = 0; | ||
| 251 | __u16 netfid = 0; | ||
| 252 | struct tcon_link *tlink; | ||
| 253 | struct cifsTconInfo *pTcon; | ||
| 254 | u8 *buf; | ||
| 255 | char *pbuf; | ||
| 256 | unsigned int bytes_read = 0; | ||
| 257 | int buf_type = CIFS_NO_BUFFER; | ||
| 258 | unsigned int link_len = 0; | ||
| 259 | FILE_ALL_INFO file_info; | ||
| 260 | |||
| 261 | if (!CIFSCouldBeMFSymlink(fattr)) | ||
| 262 | /* it's not a symlink */ | ||
| 263 | return 0; | ||
| 264 | |||
| 265 | tlink = cifs_sb_tlink(cifs_sb); | ||
| 266 | if (IS_ERR(tlink)) | ||
| 267 | return PTR_ERR(tlink); | ||
| 268 | pTcon = tlink_tcon(tlink); | ||
| 269 | |||
| 270 | rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, | ||
| 271 | CREATE_NOT_DIR, &netfid, &oplock, &file_info, | ||
| 272 | cifs_sb->local_nls, | ||
| 273 | cifs_sb->mnt_cifs_flags & | ||
| 274 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 275 | if (rc != 0) | ||
| 276 | goto out; | ||
| 277 | |||
| 278 | if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { | ||
| 279 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 280 | /* it's not a symlink */ | ||
| 281 | goto out; | ||
| 282 | } | ||
| 283 | |||
| 284 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
| 285 | if (!buf) { | ||
| 286 | rc = -ENOMEM; | ||
| 287 | goto out; | ||
| 288 | } | ||
| 289 | pbuf = buf; | ||
| 290 | |||
| 291 | rc = CIFSSMBRead(xid, pTcon, netfid, | ||
| 292 | CIFS_MF_SYMLINK_FILE_SIZE /* length */, | ||
| 293 | 0 /* offset */, | ||
| 294 | &bytes_read, &pbuf, &buf_type); | ||
| 295 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 296 | if (rc != 0) { | ||
| 297 | kfree(buf); | ||
| 298 | goto out; | ||
| 299 | } | ||
| 300 | |||
| 301 | rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); | ||
| 302 | kfree(buf); | ||
| 303 | if (rc == -EINVAL) { | ||
| 304 | /* it's not a symlink */ | ||
| 305 | rc = 0; | ||
| 306 | goto out; | ||
| 307 | } | ||
| 308 | |||
| 309 | if (rc != 0) | ||
| 310 | goto out; | ||
| 311 | |||
| 312 | /* it is a symlink */ | ||
| 313 | fattr->cf_eof = link_len; | ||
| 314 | fattr->cf_mode &= ~S_IFMT; | ||
| 315 | fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; | ||
| 316 | fattr->cf_dtype = DT_LNK; | ||
| 317 | out: | ||
| 318 | cifs_put_tlink(tlink); | ||
| 319 | return rc; | ||
| 320 | } | ||
| 31 | 321 | ||
| 32 | int | 322 | int |
| 33 | cifs_hardlink(struct dentry *old_file, struct inode *inode, | 323 | cifs_hardlink(struct dentry *old_file, struct inode *inode, |
| @@ -37,17 +327,17 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, | |||
| 37 | int xid; | 327 | int xid; |
| 38 | char *fromName = NULL; | 328 | char *fromName = NULL; |
| 39 | char *toName = NULL; | 329 | char *toName = NULL; |
| 40 | struct cifs_sb_info *cifs_sb_target; | 330 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
| 331 | struct tcon_link *tlink; | ||
| 41 | struct cifsTconInfo *pTcon; | 332 | struct cifsTconInfo *pTcon; |
| 42 | struct cifsInodeInfo *cifsInode; | 333 | struct cifsInodeInfo *cifsInode; |
| 43 | 334 | ||
| 44 | xid = GetXid(); | 335 | tlink = cifs_sb_tlink(cifs_sb); |
| 45 | 336 | if (IS_ERR(tlink)) | |
| 46 | cifs_sb_target = CIFS_SB(inode->i_sb); | 337 | return PTR_ERR(tlink); |
| 47 | pTcon = cifs_sb_target->tcon; | 338 | pTcon = tlink_tcon(tlink); |
| 48 | 339 | ||
| 49 | /* No need to check for cross device links since server will do that | 340 | xid = GetXid(); |
| 50 | BB note DFS case in future though (when we may have to check) */ | ||
| 51 | 341 | ||
| 52 | fromName = build_path_from_dentry(old_file); | 342 | fromName = build_path_from_dentry(old_file); |
| 53 | toName = build_path_from_dentry(direntry); | 343 | toName = build_path_from_dentry(direntry); |
| @@ -56,16 +346,15 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, | |||
| 56 | goto cifs_hl_exit; | 346 | goto cifs_hl_exit; |
| 57 | } | 347 | } |
| 58 | 348 | ||
| 59 | /* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/ | ||
| 60 | if (pTcon->unix_ext) | 349 | if (pTcon->unix_ext) |
| 61 | rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, | 350 | rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, |
| 62 | cifs_sb_target->local_nls, | 351 | cifs_sb->local_nls, |
| 63 | cifs_sb_target->mnt_cifs_flags & | 352 | cifs_sb->mnt_cifs_flags & |
| 64 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 353 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 65 | else { | 354 | else { |
| 66 | rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, | 355 | rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, |
| 67 | cifs_sb_target->local_nls, | 356 | cifs_sb->local_nls, |
| 68 | cifs_sb_target->mnt_cifs_flags & | 357 | cifs_sb->mnt_cifs_flags & |
| 69 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 358 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 70 | if ((rc == -EIO) || (rc == -EINVAL)) | 359 | if ((rc == -EIO) || (rc == -EINVAL)) |
| 71 | rc = -EOPNOTSUPP; | 360 | rc = -EOPNOTSUPP; |
| @@ -101,6 +390,7 @@ cifs_hl_exit: | |||
| 101 | kfree(fromName); | 390 | kfree(fromName); |
| 102 | kfree(toName); | 391 | kfree(toName); |
| 103 | FreeXid(xid); | 392 | FreeXid(xid); |
| 393 | cifs_put_tlink(tlink); | ||
| 104 | return rc; | 394 | return rc; |
| 105 | } | 395 | } |
| 106 | 396 | ||
| @@ -113,10 +403,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | |||
| 113 | char *full_path = NULL; | 403 | char *full_path = NULL; |
| 114 | char *target_path = NULL; | 404 | char *target_path = NULL; |
| 115 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | 405 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
| 116 | struct cifsTconInfo *tcon = cifs_sb->tcon; | 406 | struct tcon_link *tlink = NULL; |
| 407 | struct cifsTconInfo *tcon; | ||
| 117 | 408 | ||
| 118 | xid = GetXid(); | 409 | xid = GetXid(); |
| 119 | 410 | ||
| 411 | tlink = cifs_sb_tlink(cifs_sb); | ||
| 412 | if (IS_ERR(tlink)) { | ||
| 413 | rc = PTR_ERR(tlink); | ||
| 414 | tlink = NULL; | ||
| 415 | goto out; | ||
| 416 | } | ||
| 417 | tcon = tlink_tcon(tlink); | ||
| 418 | |||
| 120 | /* | 419 | /* |
| 121 | * For now, we just handle symlinks with unix extensions enabled. | 420 | * For now, we just handle symlinks with unix extensions enabled. |
| 122 | * Eventually we should handle NTFS reparse points, and MacOS | 421 | * Eventually we should handle NTFS reparse points, and MacOS |
| @@ -130,7 +429,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | |||
| 130 | * but there doesn't seem to be any harm in allowing the client to | 429 | * but there doesn't seem to be any harm in allowing the client to |
| 131 | * read them. | 430 | * read them. |
| 132 | */ | 431 | */ |
| 133 | if (!(tcon->ses->capabilities & CAP_UNIX)) { | 432 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) |
| 433 | && !(tcon->ses->capabilities & CAP_UNIX)) { | ||
| 134 | rc = -EACCES; | 434 | rc = -EACCES; |
| 135 | goto out; | 435 | goto out; |
| 136 | } | 436 | } |
| @@ -141,8 +441,21 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | |||
| 141 | 441 | ||
| 142 | cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); | 442 | cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); |
| 143 | 443 | ||
| 144 | rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, | 444 | rc = -EACCES; |
| 145 | cifs_sb->local_nls); | 445 | /* |
| 446 | * First try Minshall+French Symlinks, if configured | ||
| 447 | * and fallback to UNIX Extensions Symlinks. | ||
| 448 | */ | ||
| 449 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) | ||
| 450 | rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, | ||
| 451 | cifs_sb->local_nls, | ||
| 452 | cifs_sb->mnt_cifs_flags & | ||
| 453 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 454 | |||
| 455 | if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX)) | ||
| 456 | rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, | ||
| 457 | cifs_sb->local_nls); | ||
| 458 | |||
| 146 | kfree(full_path); | 459 | kfree(full_path); |
| 147 | out: | 460 | out: |
| 148 | if (rc != 0) { | 461 | if (rc != 0) { |
| @@ -151,6 +464,8 @@ out: | |||
| 151 | } | 464 | } |
| 152 | 465 | ||
| 153 | FreeXid(xid); | 466 | FreeXid(xid); |
| 467 | if (tlink) | ||
| 468 | cifs_put_tlink(tlink); | ||
| 154 | nd_set_link(nd, target_path); | 469 | nd_set_link(nd, target_path); |
| 155 | return NULL; | 470 | return NULL; |
| 156 | } | 471 | } |
| @@ -160,29 +475,37 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) | |||
| 160 | { | 475 | { |
| 161 | int rc = -EOPNOTSUPP; | 476 | int rc = -EOPNOTSUPP; |
| 162 | int xid; | 477 | int xid; |
| 163 | struct cifs_sb_info *cifs_sb; | 478 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
| 479 | struct tcon_link *tlink; | ||
| 164 | struct cifsTconInfo *pTcon; | 480 | struct cifsTconInfo *pTcon; |
| 165 | char *full_path = NULL; | 481 | char *full_path = NULL; |
| 166 | struct inode *newinode = NULL; | 482 | struct inode *newinode = NULL; |
| 167 | 483 | ||
| 168 | xid = GetXid(); | 484 | xid = GetXid(); |
| 169 | 485 | ||
| 170 | cifs_sb = CIFS_SB(inode->i_sb); | 486 | tlink = cifs_sb_tlink(cifs_sb); |
| 171 | pTcon = cifs_sb->tcon; | 487 | if (IS_ERR(tlink)) { |
| 488 | rc = PTR_ERR(tlink); | ||
| 489 | goto symlink_exit; | ||
| 490 | } | ||
| 491 | pTcon = tlink_tcon(tlink); | ||
| 172 | 492 | ||
| 173 | full_path = build_path_from_dentry(direntry); | 493 | full_path = build_path_from_dentry(direntry); |
| 174 | |||
| 175 | if (full_path == NULL) { | 494 | if (full_path == NULL) { |
| 176 | rc = -ENOMEM; | 495 | rc = -ENOMEM; |
| 177 | FreeXid(xid); | 496 | goto symlink_exit; |
| 178 | return rc; | ||
| 179 | } | 497 | } |
| 180 | 498 | ||
| 181 | cFYI(1, "Full path: %s", full_path); | 499 | cFYI(1, "Full path: %s", full_path); |
| 182 | cFYI(1, "symname is %s", symname); | 500 | cFYI(1, "symname is %s", symname); |
| 183 | 501 | ||
| 184 | /* BB what if DFS and this volume is on different share? BB */ | 502 | /* BB what if DFS and this volume is on different share? BB */ |
| 185 | if (pTcon->unix_ext) | 503 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) |
| 504 | rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, | ||
| 505 | cifs_sb->local_nls, | ||
| 506 | cifs_sb->mnt_cifs_flags & | ||
| 507 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 508 | else if (pTcon->unix_ext) | ||
| 186 | rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, | 509 | rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, |
| 187 | cifs_sb->local_nls); | 510 | cifs_sb->local_nls); |
| 188 | /* else | 511 | /* else |
| @@ -208,8 +531,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) | |||
| 208 | d_instantiate(direntry, newinode); | 531 | d_instantiate(direntry, newinode); |
| 209 | } | 532 | } |
| 210 | } | 533 | } |
| 211 | 534 | symlink_exit: | |
| 212 | kfree(full_path); | 535 | kfree(full_path); |
| 536 | cifs_put_tlink(tlink); | ||
| 213 | FreeXid(xid); | 537 | FreeXid(xid); |
| 214 | return rc; | 538 | return rc; |
| 215 | } | 539 | } |
