diff options
| author | Jim McDonough <jmcd@samba.org> | 2013-09-21 11:36:10 -0400 |
|---|---|---|
| committer | Steve French <smfrench@gmail.com> | 2013-09-21 11:36:10 -0400 |
| commit | 74d290da476f672ad756634d12aa707375d3564d (patch) | |
| tree | aa972423ddb39e39d49cca13349eb9e07c3d70c3 | |
| parent | 9ae6cf606a33b0a762798df0fb742848bcc685b5 (diff) | |
[CIFS] Provide sane values for nlink
Since we don't get info about the number of links from the readdir
linfo levels, stat() will return 0 for st_nlink, and in particular,
samba re-exported shares will show directories as files (as samba is
keying off st_nlink before evaluating how to set the dos modebits)
when doing a dir or ls.
Copy nlink to the inode, unless it wasn't provided. Provide
sane values if we don't have an existing one and none was provided.
Signed-off-by: Jim McDonough <jmcd@samba.org>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Reviewed-by: David Disseldorp <ddiss@samba.org>
Signed-off-by: Steve French <smfrench@gmail.com>
| -rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
| -rw-r--r-- | fs/cifs/inode.c | 45 | ||||
| -rw-r--r-- | fs/cifs/readdir.c | 3 |
3 files changed, 43 insertions, 6 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9c72be6fb0df..52b6f6c26bfc 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
| @@ -1268,6 +1268,7 @@ struct dfs_info3_param { | |||
| 1268 | #define CIFS_FATTR_DELETE_PENDING 0x2 | 1268 | #define CIFS_FATTR_DELETE_PENDING 0x2 |
| 1269 | #define CIFS_FATTR_NEED_REVAL 0x4 | 1269 | #define CIFS_FATTR_NEED_REVAL 0x4 |
| 1270 | #define CIFS_FATTR_INO_COLLISION 0x8 | 1270 | #define CIFS_FATTR_INO_COLLISION 0x8 |
| 1271 | #define CIFS_FATTR_UNKNOWN_NLINK 0x10 | ||
| 1271 | 1272 | ||
| 1272 | struct cifs_fattr { | 1273 | struct cifs_fattr { |
| 1273 | u32 cf_flags; | 1274 | u32 cf_flags; |
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f9ff9c173f78..867b7cdc794a 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
| @@ -120,6 +120,33 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) | |||
| 120 | cifs_i->invalid_mapping = true; | 120 | cifs_i->invalid_mapping = true; |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | /* | ||
| 124 | * copy nlink to the inode, unless it wasn't provided. Provide | ||
| 125 | * sane values if we don't have an existing one and none was provided | ||
| 126 | */ | ||
| 127 | static void | ||
| 128 | cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) | ||
| 129 | { | ||
| 130 | /* | ||
| 131 | * if we're in a situation where we can't trust what we | ||
| 132 | * got from the server (readdir, some non-unix cases) | ||
| 133 | * fake reasonable values | ||
| 134 | */ | ||
| 135 | if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { | ||
| 136 | /* only provide fake values on a new inode */ | ||
| 137 | if (inode->i_state & I_NEW) { | ||
| 138 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) | ||
| 139 | set_nlink(inode, 2); | ||
| 140 | else | ||
| 141 | set_nlink(inode, 1); | ||
| 142 | } | ||
| 143 | return; | ||
| 144 | } | ||
| 145 | |||
| 146 | /* we trust the server, so update it */ | ||
| 147 | set_nlink(inode, fattr->cf_nlink); | ||
| 148 | } | ||
| 149 | |||
| 123 | /* populate an inode with info from a cifs_fattr struct */ | 150 | /* populate an inode with info from a cifs_fattr struct */ |
| 124 | void | 151 | void |
| 125 | cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) | 152 | cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) |
| @@ -134,7 +161,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) | |||
| 134 | inode->i_mtime = fattr->cf_mtime; | 161 | inode->i_mtime = fattr->cf_mtime; |
| 135 | inode->i_ctime = fattr->cf_ctime; | 162 | inode->i_ctime = fattr->cf_ctime; |
| 136 | inode->i_rdev = fattr->cf_rdev; | 163 | inode->i_rdev = fattr->cf_rdev; |
| 137 | set_nlink(inode, fattr->cf_nlink); | 164 | cifs_nlink_fattr_to_inode(inode, fattr); |
| 138 | inode->i_uid = fattr->cf_uid; | 165 | inode->i_uid = fattr->cf_uid; |
| 139 | inode->i_gid = fattr->cf_gid; | 166 | inode->i_gid = fattr->cf_gid; |
| 140 | 167 | ||
| @@ -541,6 +568,7 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | |||
| 541 | fattr->cf_bytes = le64_to_cpu(info->AllocationSize); | 568 | fattr->cf_bytes = le64_to_cpu(info->AllocationSize); |
| 542 | fattr->cf_createtime = le64_to_cpu(info->CreationTime); | 569 | fattr->cf_createtime = le64_to_cpu(info->CreationTime); |
| 543 | 570 | ||
| 571 | fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); | ||
| 544 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { | 572 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { |
| 545 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; | 573 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; |
| 546 | fattr->cf_dtype = DT_DIR; | 574 | fattr->cf_dtype = DT_DIR; |
| @@ -548,7 +576,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | |||
| 548 | * Server can return wrong NumberOfLinks value for directories | 576 | * Server can return wrong NumberOfLinks value for directories |
| 549 | * when Unix extensions are disabled - fake it. | 577 | * when Unix extensions are disabled - fake it. |
| 550 | */ | 578 | */ |
| 551 | fattr->cf_nlink = 2; | 579 | if (!tcon->unix_ext) |
| 580 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; | ||
| 552 | } else if (fattr->cf_cifsattrs & ATTR_REPARSE) { | 581 | } else if (fattr->cf_cifsattrs & ATTR_REPARSE) { |
| 553 | fattr->cf_mode = S_IFLNK; | 582 | fattr->cf_mode = S_IFLNK; |
| 554 | fattr->cf_dtype = DT_LNK; | 583 | fattr->cf_dtype = DT_LNK; |
| @@ -561,11 +590,15 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | |||
| 561 | if (fattr->cf_cifsattrs & ATTR_READONLY) | 590 | if (fattr->cf_cifsattrs & ATTR_READONLY) |
| 562 | fattr->cf_mode &= ~(S_IWUGO); | 591 | fattr->cf_mode &= ~(S_IWUGO); |
| 563 | 592 | ||
| 564 | fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); | 593 | /* |
| 565 | if (fattr->cf_nlink < 1) { | 594 | * Don't accept zero nlink from non-unix servers unless |
| 566 | cifs_dbg(1, "replacing bogus file nlink value %u\n", | 595 | * delete is pending. Instead mark it as unknown. |
| 596 | */ | ||
| 597 | if ((fattr->cf_nlink < 1) && !tcon->unix_ext && | ||
| 598 | !info->DeletePending) { | ||
| 599 | cifs_dbg(1, "bogus file nlink value %u\n", | ||
| 567 | fattr->cf_nlink); | 600 | fattr->cf_nlink); |
| 568 | fattr->cf_nlink = 1; | 601 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; |
| 569 | } | 602 | } |
| 570 | } | 603 | } |
| 571 | 604 | ||
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 42ef03be089f..53a75f3d0179 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c | |||
| @@ -180,6 +180,9 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) | |||
| 180 | fattr->cf_dtype = DT_REG; | 180 | fattr->cf_dtype = DT_REG; |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | /* non-unix readdir doesn't provide nlink */ | ||
| 184 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; | ||
| 185 | |||
| 183 | if (fattr->cf_cifsattrs & ATTR_READONLY) | 186 | if (fattr->cf_cifsattrs & ATTR_READONLY) |
| 184 | fattr->cf_mode &= ~S_IWUGO; | 187 | fattr->cf_mode &= ~S_IWUGO; |
| 185 | 188 | ||
