diff options
Diffstat (limited to 'fs/cifs/inode.c')
| -rw-r--r-- | fs/cifs/inode.c | 570 |
1 files changed, 357 insertions, 213 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 2e904bd111c8..28a22092d450 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
| @@ -737,7 +737,7 @@ psx_del_no_retry: | |||
| 737 | /* ATTRS set to normal clears r/o bit */ | 737 | /* ATTRS set to normal clears r/o bit */ |
| 738 | pinfo_buf->Attributes = cpu_to_le32(ATTR_NORMAL); | 738 | pinfo_buf->Attributes = cpu_to_le32(ATTR_NORMAL); |
| 739 | if (!(pTcon->ses->flags & CIFS_SES_NT4)) | 739 | if (!(pTcon->ses->flags & CIFS_SES_NT4)) |
| 740 | rc = CIFSSMBSetTimes(xid, pTcon, full_path, | 740 | rc = CIFSSMBSetPathInfo(xid, pTcon, full_path, |
| 741 | pinfo_buf, | 741 | pinfo_buf, |
| 742 | cifs_sb->local_nls, | 742 | cifs_sb->local_nls, |
| 743 | cifs_sb->mnt_cifs_flags & | 743 | cifs_sb->mnt_cifs_flags & |
| @@ -767,9 +767,10 @@ psx_del_no_retry: | |||
| 767 | cifs_sb->mnt_cifs_flags & | 767 | cifs_sb->mnt_cifs_flags & |
| 768 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 768 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 769 | if (rc == 0) { | 769 | if (rc == 0) { |
| 770 | rc = CIFSSMBSetFileTimes(xid, pTcon, | 770 | rc = CIFSSMBSetFileInfo(xid, pTcon, |
| 771 | pinfo_buf, | 771 | pinfo_buf, |
| 772 | netfid); | 772 | netfid, |
| 773 | current->tgid); | ||
| 773 | CIFSSMBClose(xid, pTcon, netfid); | 774 | CIFSSMBClose(xid, pTcon, netfid); |
| 774 | } | 775 | } |
| 775 | } | 776 | } |
| @@ -984,32 +985,41 @@ mkdir_get_info: | |||
| 984 | * failed to get it from the server or was set bogus */ | 985 | * failed to get it from the server or was set bogus */ |
| 985 | if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2)) | 986 | if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2)) |
| 986 | direntry->d_inode->i_nlink = 2; | 987 | direntry->d_inode->i_nlink = 2; |
| 988 | |||
| 987 | mode &= ~current->fs->umask; | 989 | mode &= ~current->fs->umask; |
| 990 | /* must turn on setgid bit if parent dir has it */ | ||
| 991 | if (inode->i_mode & S_ISGID) | ||
| 992 | mode |= S_ISGID; | ||
| 993 | |||
| 988 | if (pTcon->unix_ext) { | 994 | if (pTcon->unix_ext) { |
| 995 | struct cifs_unix_set_info_args args = { | ||
| 996 | .mode = mode, | ||
| 997 | .ctime = NO_CHANGE_64, | ||
| 998 | .atime = NO_CHANGE_64, | ||
| 999 | .mtime = NO_CHANGE_64, | ||
| 1000 | .device = 0, | ||
| 1001 | }; | ||
| 989 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { | 1002 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { |
| 990 | CIFSSMBUnixSetPerms(xid, pTcon, full_path, | 1003 | args.uid = (__u64)current->fsuid; |
| 991 | mode, | 1004 | if (inode->i_mode & S_ISGID) |
| 992 | (__u64)current->fsuid, | 1005 | args.gid = (__u64)inode->i_gid; |
| 993 | (__u64)current->fsgid, | 1006 | else |
| 994 | 0 /* dev_t */, | 1007 | args.gid = (__u64)current->fsgid; |
| 995 | cifs_sb->local_nls, | ||
| 996 | cifs_sb->mnt_cifs_flags & | ||
| 997 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 998 | } else { | 1008 | } else { |
| 999 | CIFSSMBUnixSetPerms(xid, pTcon, full_path, | 1009 | args.uid = NO_CHANGE_64; |
| 1000 | mode, (__u64)-1, | 1010 | args.gid = NO_CHANGE_64; |
| 1001 | (__u64)-1, 0 /* dev_t */, | ||
| 1002 | cifs_sb->local_nls, | ||
| 1003 | cifs_sb->mnt_cifs_flags & | ||
| 1004 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1005 | } | 1011 | } |
| 1012 | CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args, | ||
| 1013 | cifs_sb->local_nls, | ||
| 1014 | cifs_sb->mnt_cifs_flags & | ||
| 1015 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1006 | } else { | 1016 | } else { |
| 1007 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && | 1017 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && |
| 1008 | (mode & S_IWUGO) == 0) { | 1018 | (mode & S_IWUGO) == 0) { |
| 1009 | FILE_BASIC_INFO pInfo; | 1019 | FILE_BASIC_INFO pInfo; |
| 1010 | memset(&pInfo, 0, sizeof(pInfo)); | 1020 | memset(&pInfo, 0, sizeof(pInfo)); |
| 1011 | pInfo.Attributes = cpu_to_le32(ATTR_READONLY); | 1021 | pInfo.Attributes = cpu_to_le32(ATTR_READONLY); |
| 1012 | CIFSSMBSetTimes(xid, pTcon, full_path, | 1022 | CIFSSMBSetPathInfo(xid, pTcon, full_path, |
| 1013 | &pInfo, cifs_sb->local_nls, | 1023 | &pInfo, cifs_sb->local_nls, |
| 1014 | cifs_sb->mnt_cifs_flags & | 1024 | cifs_sb->mnt_cifs_flags & |
| 1015 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 1025 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| @@ -1024,8 +1034,12 @@ mkdir_get_info: | |||
| 1024 | CIFS_MOUNT_SET_UID) { | 1034 | CIFS_MOUNT_SET_UID) { |
| 1025 | direntry->d_inode->i_uid = | 1035 | direntry->d_inode->i_uid = |
| 1026 | current->fsuid; | 1036 | current->fsuid; |
| 1027 | direntry->d_inode->i_gid = | 1037 | if (inode->i_mode & S_ISGID) |
| 1028 | current->fsgid; | 1038 | direntry->d_inode->i_gid = |
| 1039 | inode->i_gid; | ||
| 1040 | else | ||
| 1041 | direntry->d_inode->i_gid = | ||
| 1042 | current->fsgid; | ||
| 1029 | } | 1043 | } |
| 1030 | } | 1044 | } |
| 1031 | } | 1045 | } |
| @@ -1310,10 +1324,11 @@ int cifs_revalidate(struct dentry *direntry) | |||
| 1310 | /* if (S_ISDIR(direntry->d_inode->i_mode)) | 1324 | /* if (S_ISDIR(direntry->d_inode->i_mode)) |
| 1311 | shrink_dcache_parent(direntry); */ | 1325 | shrink_dcache_parent(direntry); */ |
| 1312 | if (S_ISREG(direntry->d_inode->i_mode)) { | 1326 | if (S_ISREG(direntry->d_inode->i_mode)) { |
| 1313 | if (direntry->d_inode->i_mapping) | 1327 | if (direntry->d_inode->i_mapping) { |
| 1314 | wbrc = filemap_fdatawait(direntry->d_inode->i_mapping); | 1328 | wbrc = filemap_fdatawait(direntry->d_inode->i_mapping); |
| 1315 | if (wbrc) | 1329 | if (wbrc) |
| 1316 | CIFS_I(direntry->d_inode)->write_behind_rc = wbrc; | 1330 | CIFS_I(direntry->d_inode)->write_behind_rc = wbrc; |
| 1331 | } | ||
| 1317 | /* may eventually have to do this for open files too */ | 1332 | /* may eventually have to do this for open files too */ |
| 1318 | if (list_empty(&(cifsInode->openFileList))) { | 1333 | if (list_empty(&(cifsInode->openFileList))) { |
| 1319 | /* changed on server - flush read ahead pages */ | 1334 | /* changed on server - flush read ahead pages */ |
| @@ -1413,31 +1428,304 @@ out_busy: | |||
| 1413 | return -ETXTBSY; | 1428 | return -ETXTBSY; |
| 1414 | } | 1429 | } |
| 1415 | 1430 | ||
| 1416 | int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | 1431 | static int |
| 1432 | cifs_set_file_size(struct inode *inode, struct iattr *attrs, | ||
| 1433 | int xid, char *full_path) | ||
| 1417 | { | 1434 | { |
| 1435 | int rc; | ||
| 1436 | struct cifsFileInfo *open_file; | ||
| 1437 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | ||
| 1438 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | ||
| 1439 | struct cifsTconInfo *pTcon = cifs_sb->tcon; | ||
| 1440 | |||
| 1441 | /* | ||
| 1442 | * To avoid spurious oplock breaks from server, in the case of | ||
| 1443 | * inodes that we already have open, avoid doing path based | ||
| 1444 | * setting of file size if we can do it by handle. | ||
| 1445 | * This keeps our caching token (oplock) and avoids timeouts | ||
| 1446 | * when the local oplock break takes longer to flush | ||
| 1447 | * writebehind data than the SMB timeout for the SetPathInfo | ||
| 1448 | * request would allow | ||
| 1449 | */ | ||
| 1450 | open_file = find_writable_file(cifsInode); | ||
| 1451 | if (open_file) { | ||
| 1452 | __u16 nfid = open_file->netfid; | ||
| 1453 | __u32 npid = open_file->pid; | ||
| 1454 | rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, | ||
| 1455 | npid, false); | ||
| 1456 | atomic_dec(&open_file->wrtPending); | ||
| 1457 | cFYI(1, ("SetFSize for attrs rc = %d", rc)); | ||
| 1458 | if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { | ||
| 1459 | unsigned int bytes_written; | ||
| 1460 | rc = CIFSSMBWrite(xid, pTcon, nfid, 0, attrs->ia_size, | ||
| 1461 | &bytes_written, NULL, NULL, 1); | ||
| 1462 | cFYI(1, ("Wrt seteof rc %d", rc)); | ||
| 1463 | } | ||
| 1464 | } else | ||
| 1465 | rc = -EINVAL; | ||
| 1466 | |||
| 1467 | if (rc != 0) { | ||
| 1468 | /* Set file size by pathname rather than by handle | ||
| 1469 | either because no valid, writeable file handle for | ||
| 1470 | it was found or because there was an error setting | ||
| 1471 | it by handle */ | ||
| 1472 | rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, | ||
| 1473 | false, cifs_sb->local_nls, | ||
| 1474 | cifs_sb->mnt_cifs_flags & | ||
| 1475 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1476 | cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc)); | ||
| 1477 | if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { | ||
| 1478 | __u16 netfid; | ||
| 1479 | int oplock = 0; | ||
| 1480 | |||
| 1481 | rc = SMBLegacyOpen(xid, pTcon, full_path, | ||
| 1482 | FILE_OPEN, GENERIC_WRITE, | ||
| 1483 | CREATE_NOT_DIR, &netfid, &oplock, NULL, | ||
| 1484 | cifs_sb->local_nls, | ||
| 1485 | cifs_sb->mnt_cifs_flags & | ||
| 1486 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1487 | if (rc == 0) { | ||
| 1488 | unsigned int bytes_written; | ||
| 1489 | rc = CIFSSMBWrite(xid, pTcon, netfid, 0, | ||
| 1490 | attrs->ia_size, | ||
| 1491 | &bytes_written, NULL, | ||
| 1492 | NULL, 1); | ||
| 1493 | cFYI(1, ("wrt seteof rc %d", rc)); | ||
| 1494 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 1495 | } | ||
| 1496 | } | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | if (rc == 0) { | ||
| 1500 | rc = cifs_vmtruncate(inode, attrs->ia_size); | ||
| 1501 | cifs_truncate_page(inode->i_mapping, inode->i_size); | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | return rc; | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | static int | ||
| 1508 | cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, | ||
| 1509 | char *full_path, __u32 dosattr) | ||
| 1510 | { | ||
| 1511 | int rc; | ||
| 1512 | int oplock = 0; | ||
| 1513 | __u16 netfid; | ||
| 1514 | __u32 netpid; | ||
| 1515 | bool set_time = false; | ||
| 1516 | struct cifsFileInfo *open_file; | ||
| 1517 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | ||
| 1518 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | ||
| 1519 | struct cifsTconInfo *pTcon = cifs_sb->tcon; | ||
| 1520 | FILE_BASIC_INFO info_buf; | ||
| 1521 | |||
| 1522 | if (attrs->ia_valid & ATTR_ATIME) { | ||
| 1523 | set_time = true; | ||
| 1524 | info_buf.LastAccessTime = | ||
| 1525 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); | ||
| 1526 | } else | ||
| 1527 | info_buf.LastAccessTime = 0; | ||
| 1528 | |||
| 1529 | if (attrs->ia_valid & ATTR_MTIME) { | ||
| 1530 | set_time = true; | ||
| 1531 | info_buf.LastWriteTime = | ||
| 1532 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); | ||
| 1533 | } else | ||
| 1534 | info_buf.LastWriteTime = 0; | ||
| 1535 | |||
| 1536 | /* | ||
| 1537 | * Samba throws this field away, but windows may actually use it. | ||
| 1538 | * Do not set ctime unless other time stamps are changed explicitly | ||
| 1539 | * (i.e. by utimes()) since we would then have a mix of client and | ||
| 1540 | * server times. | ||
| 1541 | */ | ||
| 1542 | if (set_time && (attrs->ia_valid & ATTR_CTIME)) { | ||
| 1543 | cFYI(1, ("CIFS - CTIME changed")); | ||
| 1544 | info_buf.ChangeTime = | ||
| 1545 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); | ||
| 1546 | } else | ||
| 1547 | info_buf.ChangeTime = 0; | ||
| 1548 | |||
| 1549 | info_buf.CreationTime = 0; /* don't change */ | ||
| 1550 | info_buf.Attributes = cpu_to_le32(dosattr); | ||
| 1551 | |||
| 1552 | /* | ||
| 1553 | * If the file is already open for write, just use that fileid | ||
| 1554 | */ | ||
| 1555 | open_file = find_writable_file(cifsInode); | ||
| 1556 | if (open_file) { | ||
| 1557 | netfid = open_file->netfid; | ||
| 1558 | netpid = open_file->pid; | ||
| 1559 | goto set_via_filehandle; | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | /* | ||
| 1563 | * NT4 apparently returns success on this call, but it doesn't | ||
| 1564 | * really work. | ||
| 1565 | */ | ||
| 1566 | if (!(pTcon->ses->flags & CIFS_SES_NT4)) { | ||
| 1567 | rc = CIFSSMBSetPathInfo(xid, pTcon, full_path, | ||
| 1568 | &info_buf, cifs_sb->local_nls, | ||
| 1569 | cifs_sb->mnt_cifs_flags & | ||
| 1570 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1571 | if (rc != -EOPNOTSUPP && rc != -EINVAL) | ||
| 1572 | goto out; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | cFYI(1, ("calling SetFileInfo since SetPathInfo for " | ||
| 1576 | "times not supported by this server")); | ||
| 1577 | rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, | ||
| 1578 | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, | ||
| 1579 | CREATE_NOT_DIR, &netfid, &oplock, | ||
| 1580 | NULL, cifs_sb->local_nls, | ||
| 1581 | cifs_sb->mnt_cifs_flags & | ||
| 1582 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1583 | |||
| 1584 | if (rc != 0) { | ||
| 1585 | if (rc == -EIO) | ||
| 1586 | rc = -EINVAL; | ||
| 1587 | goto out; | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | netpid = current->tgid; | ||
| 1591 | |||
| 1592 | set_via_filehandle: | ||
| 1593 | rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid); | ||
| 1594 | if (open_file == NULL) | ||
| 1595 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 1596 | else | ||
| 1597 | atomic_dec(&open_file->wrtPending); | ||
| 1598 | out: | ||
| 1599 | return rc; | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | static int | ||
| 1603 | cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) | ||
| 1604 | { | ||
| 1605 | int rc; | ||
| 1418 | int xid; | 1606 | int xid; |
| 1419 | struct cifs_sb_info *cifs_sb; | ||
| 1420 | struct cifsTconInfo *pTcon; | ||
| 1421 | char *full_path = NULL; | 1607 | char *full_path = NULL; |
| 1422 | int rc = -EACCES; | ||
| 1423 | struct cifsFileInfo *open_file = NULL; | ||
| 1424 | FILE_BASIC_INFO time_buf; | ||
| 1425 | bool set_time = false; | ||
| 1426 | bool set_dosattr = false; | ||
| 1427 | __u64 mode = 0xFFFFFFFFFFFFFFFFULL; | ||
| 1428 | __u64 uid = 0xFFFFFFFFFFFFFFFFULL; | ||
| 1429 | __u64 gid = 0xFFFFFFFFFFFFFFFFULL; | ||
| 1430 | struct cifsInodeInfo *cifsInode; | ||
| 1431 | struct inode *inode = direntry->d_inode; | 1608 | struct inode *inode = direntry->d_inode; |
| 1609 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | ||
| 1610 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | ||
| 1611 | struct cifsTconInfo *pTcon = cifs_sb->tcon; | ||
| 1612 | struct cifs_unix_set_info_args *args = NULL; | ||
| 1613 | |||
| 1614 | cFYI(1, ("setattr_unix on file %s attrs->ia_valid=0x%x", | ||
| 1615 | direntry->d_name.name, attrs->ia_valid)); | ||
| 1616 | |||
| 1617 | xid = GetXid(); | ||
| 1618 | |||
| 1619 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { | ||
| 1620 | /* check if we have permission to change attrs */ | ||
| 1621 | rc = inode_change_ok(inode, attrs); | ||
| 1622 | if (rc < 0) | ||
| 1623 | goto out; | ||
| 1624 | else | ||
| 1625 | rc = 0; | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | full_path = build_path_from_dentry(direntry); | ||
| 1629 | if (full_path == NULL) { | ||
| 1630 | rc = -ENOMEM; | ||
| 1631 | goto out; | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { | ||
| 1635 | /* | ||
| 1636 | Flush data before changing file size or changing the last | ||
| 1637 | write time of the file on the server. If the | ||
| 1638 | flush returns error, store it to report later and continue. | ||
| 1639 | BB: This should be smarter. Why bother flushing pages that | ||
| 1640 | will be truncated anyway? Also, should we error out here if | ||
| 1641 | the flush returns error? | ||
| 1642 | */ | ||
| 1643 | rc = filemap_write_and_wait(inode->i_mapping); | ||
| 1644 | if (rc != 0) { | ||
| 1645 | cifsInode->write_behind_rc = rc; | ||
| 1646 | rc = 0; | ||
| 1647 | } | ||
| 1648 | } | ||
| 1649 | |||
| 1650 | if (attrs->ia_valid & ATTR_SIZE) { | ||
| 1651 | rc = cifs_set_file_size(inode, attrs, xid, full_path); | ||
| 1652 | if (rc != 0) | ||
| 1653 | goto out; | ||
| 1654 | } | ||
| 1655 | |||
| 1656 | /* skip mode change if it's just for clearing setuid/setgid */ | ||
| 1657 | if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) | ||
| 1658 | attrs->ia_valid &= ~ATTR_MODE; | ||
| 1659 | |||
| 1660 | args = kmalloc(sizeof(*args), GFP_KERNEL); | ||
| 1661 | if (args == NULL) { | ||
| 1662 | rc = -ENOMEM; | ||
| 1663 | goto out; | ||
| 1664 | } | ||
| 1665 | |||
| 1666 | /* set up the struct */ | ||
| 1667 | if (attrs->ia_valid & ATTR_MODE) | ||
| 1668 | args->mode = attrs->ia_mode; | ||
| 1669 | else | ||
| 1670 | args->mode = NO_CHANGE_64; | ||
| 1671 | |||
| 1672 | if (attrs->ia_valid & ATTR_UID) | ||
| 1673 | args->uid = attrs->ia_uid; | ||
| 1674 | else | ||
| 1675 | args->uid = NO_CHANGE_64; | ||
| 1676 | |||
| 1677 | if (attrs->ia_valid & ATTR_GID) | ||
| 1678 | args->gid = attrs->ia_gid; | ||
| 1679 | else | ||
| 1680 | args->gid = NO_CHANGE_64; | ||
| 1681 | |||
| 1682 | if (attrs->ia_valid & ATTR_ATIME) | ||
| 1683 | args->atime = cifs_UnixTimeToNT(attrs->ia_atime); | ||
| 1684 | else | ||
| 1685 | args->atime = NO_CHANGE_64; | ||
| 1686 | |||
| 1687 | if (attrs->ia_valid & ATTR_MTIME) | ||
| 1688 | args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime); | ||
| 1689 | else | ||
| 1690 | args->mtime = NO_CHANGE_64; | ||
| 1691 | |||
| 1692 | if (attrs->ia_valid & ATTR_CTIME) | ||
| 1693 | args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime); | ||
| 1694 | else | ||
| 1695 | args->ctime = NO_CHANGE_64; | ||
| 1696 | |||
| 1697 | args->device = 0; | ||
| 1698 | rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path, args, | ||
| 1699 | cifs_sb->local_nls, | ||
| 1700 | cifs_sb->mnt_cifs_flags & | ||
| 1701 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1702 | |||
| 1703 | if (!rc) | ||
| 1704 | rc = inode_setattr(inode, attrs); | ||
| 1705 | out: | ||
| 1706 | kfree(args); | ||
| 1707 | kfree(full_path); | ||
| 1708 | FreeXid(xid); | ||
| 1709 | return rc; | ||
| 1710 | } | ||
| 1711 | |||
| 1712 | static int | ||
| 1713 | cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) | ||
| 1714 | { | ||
| 1715 | int xid; | ||
| 1716 | struct inode *inode = direntry->d_inode; | ||
| 1717 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | ||
| 1718 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | ||
| 1719 | char *full_path = NULL; | ||
| 1720 | int rc = -EACCES; | ||
| 1721 | __u32 dosattr = 0; | ||
| 1722 | __u64 mode = NO_CHANGE_64; | ||
| 1432 | 1723 | ||
| 1433 | xid = GetXid(); | 1724 | xid = GetXid(); |
| 1434 | 1725 | ||
| 1435 | cFYI(1, ("setattr on file %s attrs->iavalid 0x%x", | 1726 | cFYI(1, ("setattr on file %s attrs->iavalid 0x%x", |
| 1436 | direntry->d_name.name, attrs->ia_valid)); | 1727 | direntry->d_name.name, attrs->ia_valid)); |
| 1437 | 1728 | ||
| 1438 | cifs_sb = CIFS_SB(inode->i_sb); | ||
| 1439 | pTcon = cifs_sb->tcon; | ||
| 1440 | |||
| 1441 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { | 1729 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { |
| 1442 | /* check if we have permission to change attrs */ | 1730 | /* check if we have permission to change attrs */ |
| 1443 | rc = inode_change_ok(inode, attrs); | 1731 | rc = inode_change_ok(inode, attrs); |
| @@ -1453,7 +1741,6 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1453 | FreeXid(xid); | 1741 | FreeXid(xid); |
| 1454 | return -ENOMEM; | 1742 | return -ENOMEM; |
| 1455 | } | 1743 | } |
| 1456 | cifsInode = CIFS_I(inode); | ||
| 1457 | 1744 | ||
| 1458 | if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { | 1745 | if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { |
| 1459 | /* | 1746 | /* |
| @@ -1472,78 +1759,8 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1472 | } | 1759 | } |
| 1473 | 1760 | ||
| 1474 | if (attrs->ia_valid & ATTR_SIZE) { | 1761 | if (attrs->ia_valid & ATTR_SIZE) { |
| 1475 | /* To avoid spurious oplock breaks from server, in the case of | 1762 | rc = cifs_set_file_size(inode, attrs, xid, full_path); |
| 1476 | inodes that we already have open, avoid doing path based | 1763 | if (rc != 0) |
| 1477 | setting of file size if we can do it by handle. | ||
| 1478 | This keeps our caching token (oplock) and avoids timeouts | ||
| 1479 | when the local oplock break takes longer to flush | ||
| 1480 | writebehind data than the SMB timeout for the SetPathInfo | ||
| 1481 | request would allow */ | ||
| 1482 | |||
| 1483 | open_file = find_writable_file(cifsInode); | ||
| 1484 | if (open_file) { | ||
| 1485 | __u16 nfid = open_file->netfid; | ||
| 1486 | __u32 npid = open_file->pid; | ||
| 1487 | rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, | ||
| 1488 | nfid, npid, false); | ||
| 1489 | atomic_dec(&open_file->wrtPending); | ||
| 1490 | cFYI(1, ("SetFSize for attrs rc = %d", rc)); | ||
| 1491 | if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { | ||
| 1492 | unsigned int bytes_written; | ||
| 1493 | rc = CIFSSMBWrite(xid, pTcon, | ||
| 1494 | nfid, 0, attrs->ia_size, | ||
| 1495 | &bytes_written, NULL, NULL, | ||
| 1496 | 1 /* 45 seconds */); | ||
| 1497 | cFYI(1, ("Wrt seteof rc %d", rc)); | ||
| 1498 | } | ||
| 1499 | } else | ||
| 1500 | rc = -EINVAL; | ||
| 1501 | |||
| 1502 | if (rc != 0) { | ||
| 1503 | /* Set file size by pathname rather than by handle | ||
| 1504 | either because no valid, writeable file handle for | ||
| 1505 | it was found or because there was an error setting | ||
| 1506 | it by handle */ | ||
| 1507 | rc = CIFSSMBSetEOF(xid, pTcon, full_path, | ||
| 1508 | attrs->ia_size, false, | ||
| 1509 | cifs_sb->local_nls, | ||
| 1510 | cifs_sb->mnt_cifs_flags & | ||
| 1511 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1512 | cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc)); | ||
| 1513 | if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { | ||
| 1514 | __u16 netfid; | ||
| 1515 | int oplock = 0; | ||
| 1516 | |||
| 1517 | rc = SMBLegacyOpen(xid, pTcon, full_path, | ||
| 1518 | FILE_OPEN, GENERIC_WRITE, | ||
| 1519 | CREATE_NOT_DIR, &netfid, &oplock, | ||
| 1520 | NULL, cifs_sb->local_nls, | ||
| 1521 | cifs_sb->mnt_cifs_flags & | ||
| 1522 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1523 | if (rc == 0) { | ||
| 1524 | unsigned int bytes_written; | ||
| 1525 | rc = CIFSSMBWrite(xid, pTcon, | ||
| 1526 | netfid, 0, | ||
| 1527 | attrs->ia_size, | ||
| 1528 | &bytes_written, NULL, | ||
| 1529 | NULL, 1 /* 45 sec */); | ||
| 1530 | cFYI(1, ("wrt seteof rc %d", rc)); | ||
| 1531 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | } | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | /* Server is ok setting allocation size implicitly - no need | ||
| 1538 | to call: | ||
| 1539 | CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, true, | ||
| 1540 | cifs_sb->local_nls); | ||
| 1541 | */ | ||
| 1542 | |||
| 1543 | if (rc == 0) { | ||
| 1544 | rc = cifs_vmtruncate(inode, attrs->ia_size); | ||
| 1545 | cifs_truncate_page(inode->i_mapping, inode->i_size); | ||
| 1546 | } else | ||
| 1547 | goto cifs_setattr_exit; | 1764 | goto cifs_setattr_exit; |
| 1548 | } | 1765 | } |
| 1549 | 1766 | ||
| @@ -1554,21 +1771,8 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1554 | * CIFSACL support + proper Windows to Unix idmapping, we may be | 1771 | * CIFSACL support + proper Windows to Unix idmapping, we may be |
| 1555 | * able to support this in the future. | 1772 | * able to support this in the future. |
| 1556 | */ | 1773 | */ |
| 1557 | if (!pTcon->unix_ext && | 1774 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) |
| 1558 | !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) { | ||
| 1559 | attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); | 1775 | attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); |
| 1560 | } else { | ||
| 1561 | if (attrs->ia_valid & ATTR_UID) { | ||
| 1562 | cFYI(1, ("UID changed to %d", attrs->ia_uid)); | ||
| 1563 | uid = attrs->ia_uid; | ||
| 1564 | } | ||
| 1565 | if (attrs->ia_valid & ATTR_GID) { | ||
| 1566 | cFYI(1, ("GID changed to %d", attrs->ia_gid)); | ||
| 1567 | gid = attrs->ia_gid; | ||
| 1568 | } | ||
| 1569 | } | ||
| 1570 | |||
| 1571 | time_buf.Attributes = 0; | ||
| 1572 | 1776 | ||
| 1573 | /* skip mode change if it's just for clearing setuid/setgid */ | 1777 | /* skip mode change if it's just for clearing setuid/setgid */ |
| 1574 | if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) | 1778 | if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) |
| @@ -1579,13 +1783,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1579 | mode = attrs->ia_mode; | 1783 | mode = attrs->ia_mode; |
| 1580 | } | 1784 | } |
| 1581 | 1785 | ||
| 1582 | if ((pTcon->unix_ext) | 1786 | if (attrs->ia_valid & ATTR_MODE) { |
| 1583 | && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID))) | ||
| 1584 | rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, uid, gid, | ||
| 1585 | 0 /* dev_t */, cifs_sb->local_nls, | ||
| 1586 | cifs_sb->mnt_cifs_flags & | ||
| 1587 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1588 | else if (attrs->ia_valid & ATTR_MODE) { | ||
| 1589 | rc = 0; | 1787 | rc = 0; |
| 1590 | #ifdef CONFIG_CIFS_EXPERIMENTAL | 1788 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
| 1591 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) | 1789 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) |
| @@ -1594,24 +1792,19 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1594 | #endif | 1792 | #endif |
| 1595 | if (((mode & S_IWUGO) == 0) && | 1793 | if (((mode & S_IWUGO) == 0) && |
| 1596 | (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { | 1794 | (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { |
| 1597 | set_dosattr = true; | 1795 | |
| 1598 | time_buf.Attributes = cpu_to_le32(cifsInode->cifsAttrs | | 1796 | dosattr = cifsInode->cifsAttrs | ATTR_READONLY; |
| 1599 | ATTR_READONLY); | 1797 | |
| 1600 | /* fix up mode if we're not using dynperm */ | 1798 | /* fix up mode if we're not using dynperm */ |
| 1601 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) | 1799 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) |
| 1602 | attrs->ia_mode = inode->i_mode & ~S_IWUGO; | 1800 | attrs->ia_mode = inode->i_mode & ~S_IWUGO; |
| 1603 | } else if ((mode & S_IWUGO) && | 1801 | } else if ((mode & S_IWUGO) && |
| 1604 | (cifsInode->cifsAttrs & ATTR_READONLY)) { | 1802 | (cifsInode->cifsAttrs & ATTR_READONLY)) { |
| 1605 | /* If file is readonly on server, we would | 1803 | |
| 1606 | not be able to write to it - so if any write | 1804 | dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; |
| 1607 | bit is enabled for user or group or other we | 1805 | /* Attributes of 0 are ignored */ |
| 1608 | need to at least try to remove r/o dos attr */ | 1806 | if (dosattr == 0) |
| 1609 | set_dosattr = true; | 1807 | dosattr |= ATTR_NORMAL; |
| 1610 | time_buf.Attributes = cpu_to_le32(cifsInode->cifsAttrs & | ||
| 1611 | (~ATTR_READONLY)); | ||
| 1612 | /* Windows ignores set to zero */ | ||
| 1613 | if (time_buf.Attributes == 0) | ||
| 1614 | time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL); | ||
| 1615 | 1808 | ||
| 1616 | /* reset local inode permissions to normal */ | 1809 | /* reset local inode permissions to normal */ |
| 1617 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { | 1810 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { |
| @@ -1629,82 +1822,18 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1629 | } | 1822 | } |
| 1630 | } | 1823 | } |
| 1631 | 1824 | ||
| 1632 | if (attrs->ia_valid & ATTR_ATIME) { | 1825 | if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) || |
| 1633 | set_time = true; | 1826 | ((attrs->ia_valid & ATTR_MODE) && dosattr)) { |
| 1634 | time_buf.LastAccessTime = | 1827 | rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); |
| 1635 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); | 1828 | /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */ |
| 1636 | } else | ||
| 1637 | time_buf.LastAccessTime = 0; | ||
| 1638 | |||
| 1639 | if (attrs->ia_valid & ATTR_MTIME) { | ||
| 1640 | set_time = true; | ||
| 1641 | time_buf.LastWriteTime = | ||
| 1642 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); | ||
| 1643 | } else | ||
| 1644 | time_buf.LastWriteTime = 0; | ||
| 1645 | /* Do not set ctime explicitly unless other time | ||
| 1646 | stamps are changed explicitly (i.e. by utime() | ||
| 1647 | since we would then have a mix of client and | ||
| 1648 | server times */ | ||
| 1649 | |||
| 1650 | if (set_time && (attrs->ia_valid & ATTR_CTIME)) { | ||
| 1651 | set_time = true; | ||
| 1652 | /* Although Samba throws this field away | ||
| 1653 | it may be useful to Windows - but we do | ||
| 1654 | not want to set ctime unless some other | ||
| 1655 | timestamp is changing */ | ||
| 1656 | cFYI(1, ("CIFS - CTIME changed")); | ||
| 1657 | time_buf.ChangeTime = | ||
| 1658 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); | ||
| 1659 | } else | ||
| 1660 | time_buf.ChangeTime = 0; | ||
| 1661 | |||
| 1662 | if (set_time || set_dosattr) { | ||
| 1663 | time_buf.CreationTime = 0; /* do not change */ | ||
| 1664 | /* In the future we should experiment - try setting timestamps | ||
| 1665 | via Handle (SetFileInfo) instead of by path */ | ||
| 1666 | if (!(pTcon->ses->flags & CIFS_SES_NT4)) | ||
| 1667 | rc = CIFSSMBSetTimes(xid, pTcon, full_path, &time_buf, | ||
| 1668 | cifs_sb->local_nls, | ||
| 1669 | cifs_sb->mnt_cifs_flags & | ||
| 1670 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1671 | else | ||
| 1672 | rc = -EOPNOTSUPP; | ||
| 1673 | 1829 | ||
| 1674 | if (rc == -EOPNOTSUPP) { | ||
| 1675 | int oplock = 0; | ||
| 1676 | __u16 netfid; | ||
| 1677 | |||
| 1678 | cFYI(1, ("calling SetFileInfo since SetPathInfo for " | ||
| 1679 | "times not supported by this server")); | ||
| 1680 | /* BB we could scan to see if we already have it open | ||
| 1681 | and pass in pid of opener to function */ | ||
| 1682 | rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, | ||
| 1683 | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, | ||
| 1684 | CREATE_NOT_DIR, &netfid, &oplock, | ||
| 1685 | NULL, cifs_sb->local_nls, | ||
| 1686 | cifs_sb->mnt_cifs_flags & | ||
| 1687 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1688 | if (rc == 0) { | ||
| 1689 | rc = CIFSSMBSetFileTimes(xid, pTcon, &time_buf, | ||
| 1690 | netfid); | ||
| 1691 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 1692 | } else { | ||
| 1693 | /* BB For even older servers we could convert time_buf | ||
| 1694 | into old DOS style which uses two second | ||
| 1695 | granularity */ | ||
| 1696 | |||
| 1697 | /* rc = CIFSSMBSetTimesLegacy(xid, pTcon, full_path, | ||
| 1698 | &time_buf, cifs_sb->local_nls); */ | ||
| 1699 | } | ||
| 1700 | } | ||
| 1701 | /* Even if error on time set, no sense failing the call if | 1830 | /* Even if error on time set, no sense failing the call if |
| 1702 | the server would set the time to a reasonable value anyway, | 1831 | the server would set the time to a reasonable value anyway, |
| 1703 | and this check ensures that we are not being called from | 1832 | and this check ensures that we are not being called from |
| 1704 | sys_utimes in which case we ought to fail the call back to | 1833 | sys_utimes in which case we ought to fail the call back to |
| 1705 | the user when the server rejects the call */ | 1834 | the user when the server rejects the call */ |
| 1706 | if ((rc) && (attrs->ia_valid & | 1835 | if ((rc) && (attrs->ia_valid & |
| 1707 | (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) | 1836 | (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) |
| 1708 | rc = 0; | 1837 | rc = 0; |
| 1709 | } | 1838 | } |
| 1710 | 1839 | ||
| @@ -1718,6 +1847,21 @@ cifs_setattr_exit: | |||
| 1718 | return rc; | 1847 | return rc; |
| 1719 | } | 1848 | } |
| 1720 | 1849 | ||
| 1850 | int | ||
| 1851 | cifs_setattr(struct dentry *direntry, struct iattr *attrs) | ||
| 1852 | { | ||
| 1853 | struct inode *inode = direntry->d_inode; | ||
| 1854 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | ||
| 1855 | struct cifsTconInfo *pTcon = cifs_sb->tcon; | ||
| 1856 | |||
| 1857 | if (pTcon->unix_ext) | ||
| 1858 | return cifs_setattr_unix(direntry, attrs); | ||
| 1859 | |||
| 1860 | return cifs_setattr_nounix(direntry, attrs); | ||
| 1861 | |||
| 1862 | /* BB: add cifs_setattr_legacy for really old servers */ | ||
| 1863 | } | ||
| 1864 | |||
| 1721 | #if 0 | 1865 | #if 0 |
| 1722 | void cifs_delete_inode(struct inode *inode) | 1866 | void cifs_delete_inode(struct inode *inode) |
| 1723 | { | 1867 | { |
