]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
udf: Make stat on symlink report symlink length as st_size
authorJan Kara <jack@suse.cz>
Mon, 2 Jan 2017 13:30:31 +0000 (14:30 +0100)
committerJan Kara <jack@suse.cz>
Thu, 5 Jan 2017 06:52:57 +0000 (07:52 +0100)
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>
fs/udf/inode.c
fs/udf/namei.c
fs/udf/symlink.c
fs/udf/udfdecl.h

index 3a5ac2221a88d1eaa126ef5dbf10790b0e8a35a1..5f643c93f564a2efa83c1e42e81a7a3563a1407a 100644 (file)
@@ -1547,7 +1547,7 @@ reread:
                break;
        case ICBTAG_FILE_TYPE_SYMLINK:
                inode->i_data.a_ops = &udf_symlink_aops;
-               inode->i_op = &page_symlink_inode_operations;
+               inode->i_op = &udf_symlink_inode_operations;
                inode_nohighmem(inode);
                inode->i_mode = S_IFLNK | S_IRWXUGO;
                break;
index 2d65e280748bb99f2c9e494142c7cf78587148d7..babf48d0e55330ec798460c2a7500878b90eeb06 100644 (file)
@@ -931,7 +931,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
        }
 
        inode->i_data.a_ops = &udf_symlink_aops;
-       inode->i_op = &page_symlink_inode_operations;
+       inode->i_op = &udf_symlink_inode_operations;
        inode_nohighmem(inode);
 
        if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
index 8d619773056b5ef0b0a3f2d7dd27b74a6eb76434..f7dfef53f7396805c93d06c2c814423af6519b8c 100644 (file)
@@ -152,9 +152,39 @@ out_unmap:
        return err;
 }
 
+static int udf_symlink_getattr(struct vfsmount *mnt, struct dentry *dentry,
+                              struct kstat *stat)
+{
+       struct inode *inode = d_backing_inode(dentry);
+       struct page *page;
+
+       generic_fillattr(inode, stat);
+       page = read_mapping_page(inode->i_mapping, 0, NULL);
+       if (IS_ERR(page))
+               return PTR_ERR(page);
+       /*
+        * UDF uses non-trivial encoding of symlinks so i_size does not match
+        * number of characters reported by readlink(2) which apparently some
+        * applications expect. Also POSIX says that "The value returned in the
+        * st_size field shall be the length of the contents of the symbolic
+        * link, and shall not count a trailing null if one is present." So
+        * let's report the length of string returned by readlink(2) for
+        * st_size.
+        */
+       stat->size = strlen(page_address(page));
+       put_page(page);
+
+       return 0;
+}
+
 /*
  * symlinks can't do much...
  */
 const struct address_space_operations udf_symlink_aops = {
        .readpage               = udf_symlink_filler,
 };
+
+const struct inode_operations udf_symlink_inode_operations = {
+       .get_link       = page_get_link,
+       .getattr        = udf_symlink_getattr,
+};
index b608624e7089d83a71ff79111136a2dfede47ad1..63b0349843787a403bdfc25feb1f2d761603de62 100644 (file)
@@ -84,6 +84,7 @@ extern const struct inode_operations udf_dir_inode_operations;
 extern const struct file_operations udf_dir_operations;
 extern const struct inode_operations udf_file_inode_operations;
 extern const struct file_operations udf_file_operations;
+extern const struct inode_operations udf_symlink_inode_operations;
 extern const struct address_space_operations udf_aops;
 extern const struct address_space_operations udf_adinicb_aops;
 extern const struct address_space_operations udf_symlink_aops;