xref: /openbmc/linux/fs/udf/symlink.c (revision 4cfb9080)
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * symlink.c
4   *
5   * PURPOSE
6   *	Symlink handling routines for the OSTA-UDF(tm) filesystem.
7   *
8   * COPYRIGHT
9   *  (C) 1998-2001 Ben Fennema
10   *  (C) 1999 Stelias Computing Inc
11   *
12   * HISTORY
13   *
14   *  04/16/99 blf  Created.
15   *
16   */
17  
18  #include "udfdecl.h"
19  #include <linux/uaccess.h>
20  #include <linux/errno.h>
21  #include <linux/fs.h>
22  #include <linux/time.h>
23  #include <linux/mm.h>
24  #include <linux/stat.h>
25  #include <linux/pagemap.h>
26  #include "udf_i.h"
27  
28  static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
29  			  int fromlen, unsigned char *to, int tolen)
30  {
31  	struct pathComponent *pc;
32  	int elen = 0;
33  	int comp_len;
34  	unsigned char *p = to;
35  
36  	/* Reserve one byte for terminating \0 */
37  	tolen--;
38  	while (elen < fromlen) {
39  		pc = (struct pathComponent *)(from + elen);
40  		elen += sizeof(struct pathComponent);
41  		switch (pc->componentType) {
42  		case 1:
43  			/*
44  			 * Symlink points to some place which should be agreed
45   			 * upon between originator and receiver of the media. Ignore.
46  			 */
47  			if (pc->lengthComponentIdent > 0) {
48  				elen += pc->lengthComponentIdent;
49  				break;
50  			}
51  			fallthrough;
52  		case 2:
53  			if (tolen == 0)
54  				return -ENAMETOOLONG;
55  			p = to;
56  			*p++ = '/';
57  			tolen--;
58  			break;
59  		case 3:
60  			if (tolen < 3)
61  				return -ENAMETOOLONG;
62  			memcpy(p, "../", 3);
63  			p += 3;
64  			tolen -= 3;
65  			break;
66  		case 4:
67  			if (tolen < 2)
68  				return -ENAMETOOLONG;
69  			memcpy(p, "./", 2);
70  			p += 2;
71  			tolen -= 2;
72  			/* that would be . - just ignore */
73  			break;
74  		case 5:
75  			elen += pc->lengthComponentIdent;
76  			if (elen > fromlen)
77  				return -EIO;
78  			comp_len = udf_get_filename(sb, pc->componentIdent,
79  						    pc->lengthComponentIdent,
80  						    p, tolen);
81  			if (comp_len < 0)
82  				return comp_len;
83  
84  			p += comp_len;
85  			tolen -= comp_len;
86  			if (tolen == 0)
87  				return -ENAMETOOLONG;
88  			*p++ = '/';
89  			tolen--;
90  			break;
91  		}
92  	}
93  	if (p > to + 1)
94  		p[-1] = '\0';
95  	else
96  		p[0] = '\0';
97  	return 0;
98  }
99  
100  static int udf_symlink_filler(struct file *file, struct folio *folio)
101  {
102  	struct page *page = &folio->page;
103  	struct inode *inode = page->mapping->host;
104  	struct buffer_head *bh = NULL;
105  	unsigned char *symlink;
106  	int err = 0;
107  	unsigned char *p = page_address(page);
108  	struct udf_inode_info *iinfo = UDF_I(inode);
109  
110  	/* We don't support symlinks longer than one block */
111  	if (inode->i_size > inode->i_sb->s_blocksize) {
112  		err = -ENAMETOOLONG;
113  		goto out_unlock;
114  	}
115  
116  	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
117  		symlink = iinfo->i_data + iinfo->i_lenEAttr;
118  	} else {
119  		bh = udf_bread(inode, 0, 0, &err);
120  		if (!bh) {
121  			if (!err)
122  				err = -EFSCORRUPTED;
123  			goto out_err;
124  		}
125  		symlink = bh->b_data;
126  	}
127  
128  	err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
129  	brelse(bh);
130  	if (err)
131  		goto out_err;
132  
133  	SetPageUptodate(page);
134  	unlock_page(page);
135  	return 0;
136  
137  out_err:
138  	SetPageError(page);
139  out_unlock:
140  	unlock_page(page);
141  	return err;
142  }
143  
144  static int udf_symlink_getattr(struct mnt_idmap *idmap,
145  			       const struct path *path, struct kstat *stat,
146  			       u32 request_mask, unsigned int flags)
147  {
148  	struct dentry *dentry = path->dentry;
149  	struct inode *inode = d_backing_inode(dentry);
150  	struct page *page;
151  
152  	generic_fillattr(&nop_mnt_idmap, inode, stat);
153  	page = read_mapping_page(inode->i_mapping, 0, NULL);
154  	if (IS_ERR(page))
155  		return PTR_ERR(page);
156  	/*
157  	 * UDF uses non-trivial encoding of symlinks so i_size does not match
158  	 * number of characters reported by readlink(2) which apparently some
159  	 * applications expect. Also POSIX says that "The value returned in the
160  	 * st_size field shall be the length of the contents of the symbolic
161  	 * link, and shall not count a trailing null if one is present." So
162  	 * let's report the length of string returned by readlink(2) for
163  	 * st_size.
164  	 */
165  	stat->size = strlen(page_address(page));
166  	put_page(page);
167  
168  	return 0;
169  }
170  
171  /*
172   * symlinks can't do much...
173   */
174  const struct address_space_operations udf_symlink_aops = {
175  	.read_folio		= udf_symlink_filler,
176  };
177  
178  const struct inode_operations udf_symlink_inode_operations = {
179  	.get_link	= page_get_link,
180  	.getattr	= udf_symlink_getattr,
181  };
182