diff options
| -rw-r--r-- | fs/cifs/inode.c | 196 |
1 files changed, 109 insertions, 87 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f68d1abe13e6..5c722ea21133 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
| @@ -1505,6 +1505,101 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, | |||
| 1505 | } | 1505 | } |
| 1506 | 1506 | ||
| 1507 | static int | 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 | ||
| 1508 | cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) | 1603 | cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) |
| 1509 | { | 1604 | { |
| 1510 | int rc; | 1605 | int rc; |
| @@ -1623,9 +1718,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1623 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | 1718 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); |
| 1624 | char *full_path = NULL; | 1719 | char *full_path = NULL; |
| 1625 | int rc = -EACCES; | 1720 | int rc = -EACCES; |
| 1626 | FILE_BASIC_INFO time_buf; | 1721 | __u32 dosattr = 0; |
| 1627 | bool set_time = false; | ||
| 1628 | bool set_dosattr = false; | ||
| 1629 | __u64 mode = NO_CHANGE_64; | 1722 | __u64 mode = NO_CHANGE_64; |
| 1630 | 1723 | ||
| 1631 | if (pTcon->unix_ext) | 1724 | if (pTcon->unix_ext) |
| @@ -1684,8 +1777,6 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1684 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) | 1777 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) |
| 1685 | attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); | 1778 | attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); |
| 1686 | 1779 | ||
| 1687 | time_buf.Attributes = 0; | ||
| 1688 | |||
| 1689 | /* skip mode change if it's just for clearing setuid/setgid */ | 1780 | /* skip mode change if it's just for clearing setuid/setgid */ |
| 1690 | if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) | 1781 | if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) |
| 1691 | attrs->ia_valid &= ~ATTR_MODE; | 1782 | attrs->ia_valid &= ~ATTR_MODE; |
| @@ -1704,24 +1795,19 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1704 | #endif | 1795 | #endif |
| 1705 | if (((mode & S_IWUGO) == 0) && | 1796 | if (((mode & S_IWUGO) == 0) && |
| 1706 | (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { | 1797 | (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { |
| 1707 | set_dosattr = true; | 1798 | |
| 1708 | time_buf.Attributes = cpu_to_le32(cifsInode->cifsAttrs | | 1799 | dosattr = cifsInode->cifsAttrs | ATTR_READONLY; |
| 1709 | ATTR_READONLY); | 1800 | |
| 1710 | /* fix up mode if we're not using dynperm */ | 1801 | /* fix up mode if we're not using dynperm */ |
| 1711 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) | 1802 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) |
| 1712 | attrs->ia_mode = inode->i_mode & ~S_IWUGO; | 1803 | attrs->ia_mode = inode->i_mode & ~S_IWUGO; |
| 1713 | } else if ((mode & S_IWUGO) && | 1804 | } else if ((mode & S_IWUGO) && |
| 1714 | (cifsInode->cifsAttrs & ATTR_READONLY)) { | 1805 | (cifsInode->cifsAttrs & ATTR_READONLY)) { |
| 1715 | /* If file is readonly on server, we would | 1806 | |
| 1716 | not be able to write to it - so if any write | 1807 | dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; |
| 1717 | bit is enabled for user or group or other we | 1808 | /* Attributes of 0 are ignored */ |
| 1718 | need to at least try to remove r/o dos attr */ | 1809 | if (dosattr == 0) |
| 1719 | set_dosattr = true; | 1810 | dosattr |= ATTR_NORMAL; |
| 1720 | time_buf.Attributes = cpu_to_le32(cifsInode->cifsAttrs & | ||
| 1721 | (~ATTR_READONLY)); | ||
| 1722 | /* Windows ignores set to zero */ | ||
| 1723 | if (time_buf.Attributes == 0) | ||
| 1724 | time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL); | ||
| 1725 | 1811 | ||
| 1726 | /* reset local inode permissions to normal */ | 1812 | /* reset local inode permissions to normal */ |
| 1727 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { | 1813 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { |
| @@ -1739,82 +1825,18 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1739 | } | 1825 | } |
| 1740 | } | 1826 | } |
| 1741 | 1827 | ||
| 1742 | if (attrs->ia_valid & ATTR_ATIME) { | 1828 | if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) || |
| 1743 | set_time = true; | 1829 | ((attrs->ia_valid & ATTR_MODE) && dosattr)) { |
| 1744 | time_buf.LastAccessTime = | 1830 | rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); |
| 1745 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); | 1831 | /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */ |
| 1746 | } else | ||
| 1747 | time_buf.LastAccessTime = 0; | ||
| 1748 | |||
| 1749 | if (attrs->ia_valid & ATTR_MTIME) { | ||
| 1750 | set_time = true; | ||
| 1751 | time_buf.LastWriteTime = | ||
| 1752 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); | ||
| 1753 | } else | ||
| 1754 | time_buf.LastWriteTime = 0; | ||
| 1755 | /* Do not set ctime explicitly unless other time | ||
| 1756 | stamps are changed explicitly (i.e. by utime() | ||
| 1757 | since we would then have a mix of client and | ||
| 1758 | server times */ | ||
| 1759 | |||
| 1760 | if (set_time && (attrs->ia_valid & ATTR_CTIME)) { | ||
| 1761 | set_time = true; | ||
| 1762 | /* Although Samba throws this field away | ||
| 1763 | it may be useful to Windows - but we do | ||
| 1764 | not want to set ctime unless some other | ||
| 1765 | timestamp is changing */ | ||
| 1766 | cFYI(1, ("CIFS - CTIME changed")); | ||
| 1767 | time_buf.ChangeTime = | ||
| 1768 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); | ||
| 1769 | } else | ||
| 1770 | time_buf.ChangeTime = 0; | ||
| 1771 | |||
| 1772 | if (set_time || set_dosattr) { | ||
| 1773 | time_buf.CreationTime = 0; /* do not change */ | ||
| 1774 | /* In the future we should experiment - try setting timestamps | ||
| 1775 | via Handle (SetFileInfo) instead of by path */ | ||
| 1776 | if (!(pTcon->ses->flags & CIFS_SES_NT4)) | ||
| 1777 | rc = CIFSSMBSetPathInfo(xid, pTcon, full_path, | ||
| 1778 | &time_buf, cifs_sb->local_nls, | ||
| 1779 | cifs_sb->mnt_cifs_flags & | ||
| 1780 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1781 | else | ||
| 1782 | rc = -EOPNOTSUPP; | ||
| 1783 | |||
| 1784 | if (rc == -EOPNOTSUPP) { | ||
| 1785 | int oplock = 0; | ||
| 1786 | __u16 netfid; | ||
| 1787 | 1832 | ||
| 1788 | cFYI(1, ("calling SetFileInfo since SetPathInfo for " | ||
| 1789 | "times not supported by this server")); | ||
| 1790 | /* BB we could scan to see if we already have it open | ||
| 1791 | and pass in pid of opener to function */ | ||
| 1792 | rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, | ||
| 1793 | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, | ||
| 1794 | CREATE_NOT_DIR, &netfid, &oplock, | ||
| 1795 | NULL, cifs_sb->local_nls, | ||
| 1796 | cifs_sb->mnt_cifs_flags & | ||
| 1797 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1798 | if (rc == 0) { | ||
| 1799 | rc = CIFSSMBSetFileInfo(xid, pTcon, &time_buf, | ||
| 1800 | netfid, current->tgid); | ||
| 1801 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 1802 | } else { | ||
| 1803 | /* BB For even older servers we could convert time_buf | ||
| 1804 | into old DOS style which uses two second | ||
| 1805 | granularity */ | ||
| 1806 | |||
| 1807 | /* rc = CIFSSMBSetTimesLegacy(xid, pTcon, full_path, | ||
| 1808 | &time_buf, cifs_sb->local_nls); */ | ||
| 1809 | } | ||
| 1810 | } | ||
| 1811 | /* Even if error on time set, no sense failing the call if | 1833 | /* Even if error on time set, no sense failing the call if |
| 1812 | the server would set the time to a reasonable value anyway, | 1834 | the server would set the time to a reasonable value anyway, |
| 1813 | and this check ensures that we are not being called from | 1835 | and this check ensures that we are not being called from |
| 1814 | sys_utimes in which case we ought to fail the call back to | 1836 | sys_utimes in which case we ought to fail the call back to |
| 1815 | the user when the server rejects the call */ | 1837 | the user when the server rejects the call */ |
| 1816 | if ((rc) && (attrs->ia_valid & | 1838 | if ((rc) && (attrs->ia_valid & |
| 1817 | (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) | 1839 | (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) |
| 1818 | rc = 0; | 1840 | rc = 0; |
| 1819 | } | 1841 | } |
| 1820 | 1842 | ||
