diff options
Diffstat (limited to 'fs/cifs/cifssmb.c')
| -rw-r--r-- | fs/cifs/cifssmb.c | 170 |
1 files changed, 167 insertions, 3 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 611835899844..5d3f29fef532 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | #include <linux/fs.h> | 30 | #include <linux/fs.h> |
| 31 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
| 32 | #include <linux/vfs.h> | 32 | #include <linux/vfs.h> |
| 33 | #include <linux/slab.h> | ||
| 33 | #include <linux/posix_acl_xattr.h> | 34 | #include <linux/posix_acl_xattr.h> |
| 34 | #include <asm/uaccess.h> | 35 | #include <asm/uaccess.h> |
| 35 | #include "cifspdu.h" | 36 | #include "cifspdu.h" |
| @@ -500,7 +501,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) | |||
| 500 | } else if (pSMBr->hdr.WordCount == 13) { | 501 | } else if (pSMBr->hdr.WordCount == 13) { |
| 501 | cERROR(1, ("mount failed, cifs module not built " | 502 | cERROR(1, ("mount failed, cifs module not built " |
| 502 | "with CIFS_WEAK_PW_HASH support")); | 503 | "with CIFS_WEAK_PW_HASH support")); |
| 503 | rc = -EOPNOTSUPP; | 504 | rc = -EOPNOTSUPP; |
| 504 | #endif /* WEAK_PW_HASH */ | 505 | #endif /* WEAK_PW_HASH */ |
| 505 | goto neg_err_exit; | 506 | goto neg_err_exit; |
| 506 | } else if (pSMBr->hdr.WordCount != 17) { | 507 | } else if (pSMBr->hdr.WordCount != 17) { |
| @@ -1430,6 +1431,8 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, | |||
| 1430 | __u32 bytes_sent; | 1431 | __u32 bytes_sent; |
| 1431 | __u16 byte_count; | 1432 | __u16 byte_count; |
| 1432 | 1433 | ||
| 1434 | *nbytes = 0; | ||
| 1435 | |||
| 1433 | /* cFYI(1, ("write at %lld %d bytes", offset, count));*/ | 1436 | /* cFYI(1, ("write at %lld %d bytes", offset, count));*/ |
| 1434 | if (tcon->ses == NULL) | 1437 | if (tcon->ses == NULL) |
| 1435 | return -ECONNABORTED; | 1438 | return -ECONNABORTED; |
| @@ -1512,11 +1515,18 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, | |||
| 1512 | cifs_stats_inc(&tcon->num_writes); | 1515 | cifs_stats_inc(&tcon->num_writes); |
| 1513 | if (rc) { | 1516 | if (rc) { |
| 1514 | cFYI(1, ("Send error in write = %d", rc)); | 1517 | cFYI(1, ("Send error in write = %d", rc)); |
| 1515 | *nbytes = 0; | ||
| 1516 | } else { | 1518 | } else { |
| 1517 | *nbytes = le16_to_cpu(pSMBr->CountHigh); | 1519 | *nbytes = le16_to_cpu(pSMBr->CountHigh); |
| 1518 | *nbytes = (*nbytes) << 16; | 1520 | *nbytes = (*nbytes) << 16; |
| 1519 | *nbytes += le16_to_cpu(pSMBr->Count); | 1521 | *nbytes += le16_to_cpu(pSMBr->Count); |
| 1522 | |||
| 1523 | /* | ||
| 1524 | * Mask off high 16 bits when bytes written as returned by the | ||
| 1525 | * server is greater than bytes requested by the client. Some | ||
| 1526 | * OS/2 servers are known to set incorrect CountHigh values. | ||
| 1527 | */ | ||
| 1528 | if (*nbytes > count) | ||
| 1529 | *nbytes &= 0xFFFF; | ||
| 1520 | } | 1530 | } |
| 1521 | 1531 | ||
| 1522 | cifs_buf_release(pSMB); | 1532 | cifs_buf_release(pSMB); |
| @@ -1605,6 +1615,14 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, | |||
| 1605 | *nbytes = le16_to_cpu(pSMBr->CountHigh); | 1615 | *nbytes = le16_to_cpu(pSMBr->CountHigh); |
| 1606 | *nbytes = (*nbytes) << 16; | 1616 | *nbytes = (*nbytes) << 16; |
| 1607 | *nbytes += le16_to_cpu(pSMBr->Count); | 1617 | *nbytes += le16_to_cpu(pSMBr->Count); |
| 1618 | |||
| 1619 | /* | ||
| 1620 | * Mask off high 16 bits when bytes written as returned by the | ||
| 1621 | * server is greater than bytes requested by the client. OS/2 | ||
| 1622 | * servers are known to set incorrect CountHigh values. | ||
| 1623 | */ | ||
| 1624 | if (*nbytes > count) | ||
| 1625 | *nbytes &= 0xFFFF; | ||
| 1608 | } | 1626 | } |
| 1609 | 1627 | ||
| 1610 | /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ | 1628 | /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ |
| @@ -1793,8 +1811,21 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, | |||
| 1793 | } | 1811 | } |
| 1794 | parm_data = (struct cifs_posix_lock *) | 1812 | parm_data = (struct cifs_posix_lock *) |
| 1795 | ((char *)&pSMBr->hdr.Protocol + data_offset); | 1813 | ((char *)&pSMBr->hdr.Protocol + data_offset); |
| 1796 | if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) | 1814 | if (parm_data->lock_type == __constant_cpu_to_le16(CIFS_UNLCK)) |
| 1797 | pLockData->fl_type = F_UNLCK; | 1815 | pLockData->fl_type = F_UNLCK; |
| 1816 | else { | ||
| 1817 | if (parm_data->lock_type == | ||
| 1818 | __constant_cpu_to_le16(CIFS_RDLCK)) | ||
| 1819 | pLockData->fl_type = F_RDLCK; | ||
| 1820 | else if (parm_data->lock_type == | ||
| 1821 | __constant_cpu_to_le16(CIFS_WRLCK)) | ||
| 1822 | pLockData->fl_type = F_WRLCK; | ||
| 1823 | |||
| 1824 | pLockData->fl_start = parm_data->start; | ||
| 1825 | pLockData->fl_end = parm_data->start + | ||
| 1826 | parm_data->length - 1; | ||
| 1827 | pLockData->fl_pid = parm_data->pid; | ||
| 1828 | } | ||
| 1798 | } | 1829 | } |
| 1799 | 1830 | ||
| 1800 | plk_err_exit: | 1831 | plk_err_exit: |
| @@ -3230,8 +3261,72 @@ QInfRetry: | |||
| 3230 | return rc; | 3261 | return rc; |
| 3231 | } | 3262 | } |
| 3232 | 3263 | ||
| 3264 | int | ||
| 3265 | CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon, | ||
| 3266 | u16 netfid, FILE_ALL_INFO *pFindData) | ||
| 3267 | { | ||
| 3268 | struct smb_t2_qfi_req *pSMB = NULL; | ||
| 3269 | struct smb_t2_qfi_rsp *pSMBr = NULL; | ||
| 3270 | int rc = 0; | ||
| 3271 | int bytes_returned; | ||
| 3272 | __u16 params, byte_count; | ||
| 3273 | |||
| 3274 | QFileInfoRetry: | ||
| 3275 | rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, | ||
| 3276 | (void **) &pSMBr); | ||
| 3277 | if (rc) | ||
| 3278 | return rc; | ||
| 3279 | |||
| 3280 | params = 2 /* level */ + 2 /* fid */; | ||
| 3281 | pSMB->t2.TotalDataCount = 0; | ||
| 3282 | pSMB->t2.MaxParameterCount = cpu_to_le16(4); | ||
| 3283 | /* BB find exact max data count below from sess structure BB */ | ||
| 3284 | pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); | ||
| 3285 | pSMB->t2.MaxSetupCount = 0; | ||
| 3286 | pSMB->t2.Reserved = 0; | ||
| 3287 | pSMB->t2.Flags = 0; | ||
| 3288 | pSMB->t2.Timeout = 0; | ||
| 3289 | pSMB->t2.Reserved2 = 0; | ||
| 3290 | pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, | ||
| 3291 | Fid) - 4); | ||
| 3292 | pSMB->t2.DataCount = 0; | ||
| 3293 | pSMB->t2.DataOffset = 0; | ||
| 3294 | pSMB->t2.SetupCount = 1; | ||
| 3295 | pSMB->t2.Reserved3 = 0; | ||
| 3296 | pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); | ||
| 3297 | byte_count = params + 1 /* pad */ ; | ||
| 3298 | pSMB->t2.TotalParameterCount = cpu_to_le16(params); | ||
| 3299 | pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; | ||
| 3300 | pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); | ||
| 3301 | pSMB->Pad = 0; | ||
| 3302 | pSMB->Fid = netfid; | ||
| 3303 | pSMB->hdr.smb_buf_length += byte_count; | ||
| 3304 | |||
| 3305 | rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, | ||
| 3306 | (struct smb_hdr *) pSMBr, &bytes_returned, 0); | ||
| 3307 | if (rc) { | ||
| 3308 | cFYI(1, ("Send error in QPathInfo = %d", rc)); | ||
| 3309 | } else { /* decode response */ | ||
| 3310 | rc = validate_t2((struct smb_t2_rsp *)pSMBr); | ||
| 3233 | 3311 | ||
| 3312 | if (rc) /* BB add auto retry on EOPNOTSUPP? */ | ||
| 3313 | rc = -EIO; | ||
| 3314 | else if (pSMBr->ByteCount < 40) | ||
| 3315 | rc = -EIO; /* bad smb */ | ||
| 3316 | else if (pFindData) { | ||
| 3317 | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); | ||
| 3318 | memcpy((char *) pFindData, | ||
| 3319 | (char *) &pSMBr->hdr.Protocol + | ||
| 3320 | data_offset, sizeof(FILE_ALL_INFO)); | ||
| 3321 | } else | ||
| 3322 | rc = -ENOMEM; | ||
| 3323 | } | ||
| 3324 | cifs_buf_release(pSMB); | ||
| 3325 | if (rc == -EAGAIN) | ||
| 3326 | goto QFileInfoRetry; | ||
| 3234 | 3327 | ||
| 3328 | return rc; | ||
| 3329 | } | ||
| 3235 | 3330 | ||
| 3236 | int | 3331 | int |
| 3237 | CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, | 3332 | CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, |
| @@ -3335,6 +3430,75 @@ QPathInfoRetry: | |||
| 3335 | } | 3430 | } |
| 3336 | 3431 | ||
| 3337 | int | 3432 | int |
| 3433 | CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon, | ||
| 3434 | u16 netfid, FILE_UNIX_BASIC_INFO *pFindData) | ||
| 3435 | { | ||
| 3436 | struct smb_t2_qfi_req *pSMB = NULL; | ||
| 3437 | struct smb_t2_qfi_rsp *pSMBr = NULL; | ||
| 3438 | int rc = 0; | ||
| 3439 | int bytes_returned; | ||
| 3440 | __u16 params, byte_count; | ||
| 3441 | |||
| 3442 | UnixQFileInfoRetry: | ||
| 3443 | rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, | ||
| 3444 | (void **) &pSMBr); | ||
| 3445 | if (rc) | ||
| 3446 | return rc; | ||
| 3447 | |||
| 3448 | params = 2 /* level */ + 2 /* fid */; | ||
| 3449 | pSMB->t2.TotalDataCount = 0; | ||
| 3450 | pSMB->t2.MaxParameterCount = cpu_to_le16(4); | ||
| 3451 | /* BB find exact max data count below from sess structure BB */ | ||
| 3452 | pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); | ||
| 3453 | pSMB->t2.MaxSetupCount = 0; | ||
| 3454 | pSMB->t2.Reserved = 0; | ||
| 3455 | pSMB->t2.Flags = 0; | ||
| 3456 | pSMB->t2.Timeout = 0; | ||
| 3457 | pSMB->t2.Reserved2 = 0; | ||
| 3458 | pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, | ||
| 3459 | Fid) - 4); | ||
| 3460 | pSMB->t2.DataCount = 0; | ||
| 3461 | pSMB->t2.DataOffset = 0; | ||
| 3462 | pSMB->t2.SetupCount = 1; | ||
| 3463 | pSMB->t2.Reserved3 = 0; | ||
| 3464 | pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); | ||
| 3465 | byte_count = params + 1 /* pad */ ; | ||
| 3466 | pSMB->t2.TotalParameterCount = cpu_to_le16(params); | ||
| 3467 | pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; | ||
| 3468 | pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); | ||
| 3469 | pSMB->Pad = 0; | ||
| 3470 | pSMB->Fid = netfid; | ||
| 3471 | pSMB->hdr.smb_buf_length += byte_count; | ||
| 3472 | |||
| 3473 | rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, | ||
| 3474 | (struct smb_hdr *) pSMBr, &bytes_returned, 0); | ||
| 3475 | if (rc) { | ||
| 3476 | cFYI(1, ("Send error in QPathInfo = %d", rc)); | ||
| 3477 | } else { /* decode response */ | ||
| 3478 | rc = validate_t2((struct smb_t2_rsp *)pSMBr); | ||
| 3479 | |||
| 3480 | if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) { | ||
| 3481 | cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n" | ||
| 3482 | "Unix Extensions can be disabled on mount " | ||
| 3483 | "by specifying the nosfu mount option.")); | ||
| 3484 | rc = -EIO; /* bad smb */ | ||
| 3485 | } else { | ||
| 3486 | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); | ||
| 3487 | memcpy((char *) pFindData, | ||
| 3488 | (char *) &pSMBr->hdr.Protocol + | ||
| 3489 | data_offset, | ||
| 3490 | sizeof(FILE_UNIX_BASIC_INFO)); | ||
| 3491 | } | ||
| 3492 | } | ||
| 3493 | |||
| 3494 | cifs_buf_release(pSMB); | ||
| 3495 | if (rc == -EAGAIN) | ||
| 3496 | goto UnixQFileInfoRetry; | ||
| 3497 | |||
| 3498 | return rc; | ||
| 3499 | } | ||
| 3500 | |||
| 3501 | int | ||
| 3338 | CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, | 3502 | CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, |
| 3339 | const unsigned char *searchName, | 3503 | const unsigned char *searchName, |
| 3340 | FILE_UNIX_BASIC_INFO *pFindData, | 3504 | FILE_UNIX_BASIC_INFO *pFindData, |
