diff options
author | Jan Kara <jack@suse.cz> | 2017-01-02 08:30:31 -0500 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2017-01-05 01:52:57 -0500 |
commit | ad4d05329df5e9825cac3132e12453a6c12915b8 (patch) | |
tree | 56e6acd3b189c2fa6654ee13d1418616de19c7e8 /fs/udf | |
parent | a17f0cb5b9eaf8212b396d2381cf7594cd5315c7 (diff) |
udf: Make stat on symlink report symlink length as st_size
UDF encodes symlinks in a more complex fashion and thus i_size of a
symlink does not match the lenght of a string returned by readlink(2).
This confuses some applications (see bug 191241) and may be considered a
violation of POSIX. Fix the problem by reading the link into page cache
in response to stat(2) call and report the length of the decoded path.
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/udf')
-rw-r--r-- | fs/udf/inode.c | 2 | ||||
-rw-r--r-- | fs/udf/namei.c | 2 | ||||
-rw-r--r-- | fs/udf/symlink.c | 30 | ||||
-rw-r--r-- | fs/udf/udfdecl.h | 1 |
4 files changed, 33 insertions, 2 deletions
diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 3a5ac2221a88..5f643c93f564 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c | |||
@@ -1547,7 +1547,7 @@ reread: | |||
1547 | break; | 1547 | break; |
1548 | case ICBTAG_FILE_TYPE_SYMLINK: | 1548 | case ICBTAG_FILE_TYPE_SYMLINK: |
1549 | inode->i_data.a_ops = &udf_symlink_aops; | 1549 | inode->i_data.a_ops = &udf_symlink_aops; |
1550 | inode->i_op = &page_symlink_inode_operations; | 1550 | inode->i_op = &udf_symlink_inode_operations; |
1551 | inode_nohighmem(inode); | 1551 | inode_nohighmem(inode); |
1552 | inode->i_mode = S_IFLNK | S_IRWXUGO; | 1552 | inode->i_mode = S_IFLNK | S_IRWXUGO; |
1553 | break; | 1553 | break; |
diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 2d65e280748b..babf48d0e553 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c | |||
@@ -931,7 +931,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry, | |||
931 | } | 931 | } |
932 | 932 | ||
933 | inode->i_data.a_ops = &udf_symlink_aops; | 933 | inode->i_data.a_ops = &udf_symlink_aops; |
934 | inode->i_op = &page_symlink_inode_operations; | 934 | inode->i_op = &udf_symlink_inode_operations; |
935 | inode_nohighmem(inode); | 935 | inode_nohighmem(inode); |
936 | 936 | ||
937 | if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { | 937 | if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { |
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 8d619773056b..f7dfef53f739 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c | |||
@@ -152,9 +152,39 @@ out_unmap: | |||
152 | return err; | 152 | return err; |
153 | } | 153 | } |
154 | 154 | ||
155 | static int udf_symlink_getattr(struct vfsmount *mnt, struct dentry *dentry, | ||
156 | struct kstat *stat) | ||
157 | { | ||
158 | struct inode *inode = d_backing_inode(dentry); | ||
159 | struct page *page; | ||
160 | |||
161 | generic_fillattr(inode, stat); | ||
162 | page = read_mapping_page(inode->i_mapping, 0, NULL); | ||
163 | if (IS_ERR(page)) | ||
164 | return PTR_ERR(page); | ||
165 | /* | ||
166 | * UDF uses non-trivial encoding of symlinks so i_size does not match | ||
167 | * number of characters reported by readlink(2) which apparently some | ||
168 | * applications expect. Also POSIX says that "The value returned in the | ||
169 | * st_size field shall be the length of the contents of the symbolic | ||
170 | * link, and shall not count a trailing null if one is present." So | ||
171 | * let's report the length of string returned by readlink(2) for | ||
172 | * st_size. | ||
173 | */ | ||
174 | stat->size = strlen(page_address(page)); | ||
175 | put_page(page); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
155 | /* | 180 | /* |
156 | * symlinks can't do much... | 181 | * symlinks can't do much... |
157 | */ | 182 | */ |
158 | const struct address_space_operations udf_symlink_aops = { | 183 | const struct address_space_operations udf_symlink_aops = { |
159 | .readpage = udf_symlink_filler, | 184 | .readpage = udf_symlink_filler, |
160 | }; | 185 | }; |
186 | |||
187 | const struct inode_operations udf_symlink_inode_operations = { | ||
188 | .get_link = page_get_link, | ||
189 | .getattr = udf_symlink_getattr, | ||
190 | }; | ||
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index b608624e7089..63b034984378 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h | |||
@@ -84,6 +84,7 @@ extern const struct inode_operations udf_dir_inode_operations; | |||
84 | extern const struct file_operations udf_dir_operations; | 84 | extern const struct file_operations udf_dir_operations; |
85 | extern const struct inode_operations udf_file_inode_operations; | 85 | extern const struct inode_operations udf_file_inode_operations; |
86 | extern const struct file_operations udf_file_operations; | 86 | extern const struct file_operations udf_file_operations; |
87 | extern const struct inode_operations udf_symlink_inode_operations; | ||
87 | extern const struct address_space_operations udf_aops; | 88 | extern const struct address_space_operations udf_aops; |
88 | extern const struct address_space_operations udf_adinicb_aops; | 89 | extern const struct address_space_operations udf_adinicb_aops; |
89 | extern const struct address_space_operations udf_symlink_aops; | 90 | extern const struct address_space_operations udf_symlink_aops; |