diff options
Diffstat (limited to 'fs')
-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 | ||