diff options
Diffstat (limited to 'fs/cifs/inode.c')
| -rw-r--r-- | fs/cifs/inode.c | 168 |
1 files changed, 97 insertions, 71 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 129dbfe4dca7..2e904bd111c8 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
| @@ -219,15 +219,15 @@ int cifs_get_inode_info_unix(struct inode **pinode, | |||
| 219 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &find_data, | 219 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &find_data, |
| 220 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | 220 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & |
| 221 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 221 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 222 | if (rc) { | 222 | if (rc == -EREMOTE && !is_dfs_referral) { |
| 223 | if (rc == -EREMOTE && !is_dfs_referral) { | 223 | is_dfs_referral = true; |
| 224 | is_dfs_referral = true; | 224 | cFYI(DBG2, ("DFS ref")); |
| 225 | cFYI(DBG2, ("DFS ref")); | 225 | /* for DFS, server does not give us real inode data */ |
| 226 | /* for DFS, server does not give us real inode data */ | 226 | fill_fake_finddataunix(&find_data, sb); |
| 227 | fill_fake_finddataunix(&find_data, sb); | 227 | rc = 0; |
| 228 | rc = 0; | 228 | } else if (rc) |
| 229 | } | 229 | goto cgiiu_exit; |
| 230 | } | 230 | |
| 231 | num_of_bytes = le64_to_cpu(find_data.NumOfBytes); | 231 | num_of_bytes = le64_to_cpu(find_data.NumOfBytes); |
| 232 | end_of_file = le64_to_cpu(find_data.EndOfFile); | 232 | end_of_file = le64_to_cpu(find_data.EndOfFile); |
| 233 | 233 | ||
| @@ -236,7 +236,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, | |||
| 236 | *pinode = new_inode(sb); | 236 | *pinode = new_inode(sb); |
| 237 | if (*pinode == NULL) { | 237 | if (*pinode == NULL) { |
| 238 | rc = -ENOMEM; | 238 | rc = -ENOMEM; |
| 239 | goto cgiiu_exit; | 239 | goto cgiiu_exit; |
| 240 | } | 240 | } |
| 241 | /* Is an i_ino of zero legal? */ | 241 | /* Is an i_ino of zero legal? */ |
| 242 | /* note ino incremented to unique num in new_inode */ | 242 | /* note ino incremented to unique num in new_inode */ |
| @@ -418,6 +418,7 @@ int cifs_get_inode_info(struct inode **pinode, | |||
| 418 | char *buf = NULL; | 418 | char *buf = NULL; |
| 419 | bool adjustTZ = false; | 419 | bool adjustTZ = false; |
| 420 | bool is_dfs_referral = false; | 420 | bool is_dfs_referral = false; |
| 421 | umode_t default_mode; | ||
| 421 | 422 | ||
| 422 | pTcon = cifs_sb->tcon; | 423 | pTcon = cifs_sb->tcon; |
| 423 | cFYI(1, ("Getting info on %s", full_path)); | 424 | cFYI(1, ("Getting info on %s", full_path)); |
| @@ -530,47 +531,42 @@ int cifs_get_inode_info(struct inode **pinode, | |||
| 530 | inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; | 531 | inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; |
| 531 | } | 532 | } |
| 532 | 533 | ||
| 533 | /* set default mode. will override for dirs below */ | 534 | /* get default inode mode */ |
| 534 | if (atomic_read(&cifsInfo->inUse) == 0) | 535 | if (attr & ATTR_DIRECTORY) |
| 535 | /* new inode, can safely set these fields */ | 536 | default_mode = cifs_sb->mnt_dir_mode; |
| 536 | inode->i_mode = cifs_sb->mnt_file_mode; | 537 | else |
| 537 | else /* since we set the inode type below we need to mask off | 538 | default_mode = cifs_sb->mnt_file_mode; |
| 538 | to avoid strange results if type changes and both | 539 | |
| 539 | get orred in */ | 540 | /* set permission bits */ |
| 540 | inode->i_mode &= ~S_IFMT; | 541 | if (atomic_read(&cifsInfo->inUse) == 0 || |
| 541 | /* if (attr & ATTR_REPARSE) */ | 542 | (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) |
| 542 | /* We no longer handle these as symlinks because we could not | 543 | inode->i_mode = default_mode; |
| 543 | follow them due to the absolute path with drive letter */ | 544 | else { |
| 544 | if (attr & ATTR_DIRECTORY) { | 545 | /* just reenable write bits if !ATTR_READONLY */ |
| 545 | /* override default perms since we do not do byte range locking | 546 | if ((inode->i_mode & S_IWUGO) == 0 && |
| 546 | on dirs */ | 547 | (attr & ATTR_READONLY) == 0) |
| 547 | inode->i_mode = cifs_sb->mnt_dir_mode; | 548 | inode->i_mode |= (S_IWUGO & default_mode); |
| 548 | inode->i_mode |= S_IFDIR; | 549 | inode->i_mode &= ~S_IFMT; |
| 549 | } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && | 550 | } |
| 550 | (cifsInfo->cifsAttrs & ATTR_SYSTEM) && | 551 | /* clear write bits if ATTR_READONLY is set */ |
| 551 | /* No need to le64 convert size of zero */ | 552 | if (attr & ATTR_READONLY) |
| 552 | (pfindData->EndOfFile == 0)) { | 553 | inode->i_mode &= ~S_IWUGO; |
| 553 | inode->i_mode = cifs_sb->mnt_file_mode; | 554 | |
| 554 | inode->i_mode |= S_IFIFO; | 555 | /* set inode type */ |
| 555 | /* BB Finish for SFU style symlinks and devices */ | 556 | if ((attr & ATTR_SYSTEM) && |
| 556 | } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && | 557 | (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) { |
| 557 | (cifsInfo->cifsAttrs & ATTR_SYSTEM)) { | 558 | /* no need to fix endianness on 0 */ |
| 558 | if (decode_sfu_inode(inode, le64_to_cpu(pfindData->EndOfFile), | 559 | if (pfindData->EndOfFile == 0) |
| 559 | full_path, cifs_sb, xid)) | 560 | inode->i_mode |= S_IFIFO; |
| 560 | cFYI(1, ("Unrecognized sfu inode type")); | 561 | else if (decode_sfu_inode(inode, |
| 561 | 562 | le64_to_cpu(pfindData->EndOfFile), | |
| 562 | cFYI(1, ("sfu mode 0%o", inode->i_mode)); | 563 | full_path, cifs_sb, xid)) |
| 564 | cFYI(1, ("unknown SFU file type\n")); | ||
| 563 | } else { | 565 | } else { |
| 564 | inode->i_mode |= S_IFREG; | 566 | if (attr & ATTR_DIRECTORY) |
| 565 | /* treat dos attribute of read-only as read-only mode eg 555 */ | 567 | inode->i_mode |= S_IFDIR; |
| 566 | if (cifsInfo->cifsAttrs & ATTR_READONLY) | 568 | else |
| 567 | inode->i_mode &= ~(S_IWUGO); | 569 | inode->i_mode |= S_IFREG; |
| 568 | else if ((inode->i_mode & S_IWUGO) == 0) | ||
| 569 | /* the ATTR_READONLY flag may have been */ | ||
| 570 | /* changed on server -- set any w bits */ | ||
| 571 | /* allowed by mnt_file_mode */ | ||
| 572 | inode->i_mode |= (S_IWUGO & cifs_sb->mnt_file_mode); | ||
| 573 | /* BB add code to validate if device or weird share or device type? */ | ||
| 574 | } | 570 | } |
| 575 | 571 | ||
| 576 | spin_lock(&inode->i_lock); | 572 | spin_lock(&inode->i_lock); |
| @@ -1019,8 +1015,11 @@ mkdir_get_info: | |||
| 1019 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 1015 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 1020 | } | 1016 | } |
| 1021 | if (direntry->d_inode) { | 1017 | if (direntry->d_inode) { |
| 1022 | direntry->d_inode->i_mode = mode; | 1018 | if (cifs_sb->mnt_cifs_flags & |
| 1023 | direntry->d_inode->i_mode |= S_IFDIR; | 1019 | CIFS_MOUNT_DYNPERM) |
| 1020 | direntry->d_inode->i_mode = | ||
| 1021 | (mode | S_IFDIR); | ||
| 1022 | |||
| 1024 | if (cifs_sb->mnt_cifs_flags & | 1023 | if (cifs_sb->mnt_cifs_flags & |
| 1025 | CIFS_MOUNT_SET_UID) { | 1024 | CIFS_MOUNT_SET_UID) { |
| 1026 | direntry->d_inode->i_uid = | 1025 | direntry->d_inode->i_uid = |
| @@ -1547,13 +1546,26 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1547 | } else | 1546 | } else |
| 1548 | goto cifs_setattr_exit; | 1547 | goto cifs_setattr_exit; |
| 1549 | } | 1548 | } |
| 1550 | if (attrs->ia_valid & ATTR_UID) { | 1549 | |
| 1551 | cFYI(1, ("UID changed to %d", attrs->ia_uid)); | 1550 | /* |
| 1552 | uid = attrs->ia_uid; | 1551 | * Without unix extensions we can't send ownership changes to the |
| 1553 | } | 1552 | * server, so silently ignore them. This is consistent with how |
| 1554 | if (attrs->ia_valid & ATTR_GID) { | 1553 | * local DOS/Windows filesystems behave (VFAT, NTFS, etc). With |
| 1555 | cFYI(1, ("GID changed to %d", attrs->ia_gid)); | 1554 | * CIFSACL support + proper Windows to Unix idmapping, we may be |
| 1556 | gid = attrs->ia_gid; | 1555 | * able to support this in the future. |
| 1556 | */ | ||
| 1557 | if (!pTcon->unix_ext && | ||
| 1558 | !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) { | ||
| 1559 | 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 | } | ||
| 1557 | } | 1569 | } |
| 1558 | 1570 | ||
| 1559 | time_buf.Attributes = 0; | 1571 | time_buf.Attributes = 0; |
| @@ -1563,7 +1575,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1563 | attrs->ia_valid &= ~ATTR_MODE; | 1575 | attrs->ia_valid &= ~ATTR_MODE; |
| 1564 | 1576 | ||
| 1565 | if (attrs->ia_valid & ATTR_MODE) { | 1577 | if (attrs->ia_valid & ATTR_MODE) { |
| 1566 | cFYI(1, ("Mode changed to 0x%x", attrs->ia_mode)); | 1578 | cFYI(1, ("Mode changed to 0%o", attrs->ia_mode)); |
| 1567 | mode = attrs->ia_mode; | 1579 | mode = attrs->ia_mode; |
| 1568 | } | 1580 | } |
| 1569 | 1581 | ||
| @@ -1578,18 +1590,18 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1578 | #ifdef CONFIG_CIFS_EXPERIMENTAL | 1590 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
| 1579 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) | 1591 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) |
| 1580 | rc = mode_to_acl(inode, full_path, mode); | 1592 | rc = mode_to_acl(inode, full_path, mode); |
| 1581 | else if ((mode & S_IWUGO) == 0) { | 1593 | else |
| 1582 | #else | ||
| 1583 | if ((mode & S_IWUGO) == 0) { | ||
| 1584 | #endif | 1594 | #endif |
| 1585 | /* not writeable */ | 1595 | if (((mode & S_IWUGO) == 0) && |
| 1586 | if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) { | 1596 | (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { |
| 1587 | set_dosattr = true; | 1597 | set_dosattr = true; |
| 1588 | time_buf.Attributes = | 1598 | time_buf.Attributes = cpu_to_le32(cifsInode->cifsAttrs | |
| 1589 | cpu_to_le32(cifsInode->cifsAttrs | | 1599 | ATTR_READONLY); |
| 1590 | ATTR_READONLY); | 1600 | /* fix up mode if we're not using dynperm */ |
| 1591 | } | 1601 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) |
| 1592 | } else if (cifsInode->cifsAttrs & ATTR_READONLY) { | 1602 | attrs->ia_mode = inode->i_mode & ~S_IWUGO; |
| 1603 | } else if ((mode & S_IWUGO) && | ||
| 1604 | (cifsInode->cifsAttrs & ATTR_READONLY)) { | ||
| 1593 | /* If file is readonly on server, we would | 1605 | /* If file is readonly on server, we would |
| 1594 | not be able to write to it - so if any write | 1606 | not be able to write to it - so if any write |
| 1595 | bit is enabled for user or group or other we | 1607 | bit is enabled for user or group or other we |
| @@ -1600,6 +1612,20 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
| 1600 | /* Windows ignores set to zero */ | 1612 | /* Windows ignores set to zero */ |
| 1601 | if (time_buf.Attributes == 0) | 1613 | if (time_buf.Attributes == 0) |
| 1602 | time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL); | 1614 | time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL); |
| 1615 | |||
| 1616 | /* reset local inode permissions to normal */ | ||
| 1617 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { | ||
| 1618 | attrs->ia_mode &= ~(S_IALLUGO); | ||
| 1619 | if (S_ISDIR(inode->i_mode)) | ||
| 1620 | attrs->ia_mode |= | ||
| 1621 | cifs_sb->mnt_dir_mode; | ||
| 1622 | else | ||
| 1623 | attrs->ia_mode |= | ||
| 1624 | cifs_sb->mnt_file_mode; | ||
| 1625 | } | ||
| 1626 | } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { | ||
| 1627 | /* ignore mode change - ATTR_READONLY hasn't changed */ | ||
| 1628 | attrs->ia_valid &= ~ATTR_MODE; | ||
| 1603 | } | 1629 | } |
| 1604 | } | 1630 | } |
| 1605 | 1631 | ||
