xref: /openbmc/linux/fs/udf/symlink.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
15ce34554SBagas Sanjaya // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * symlink.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * PURPOSE
61da177e4SLinus Torvalds  *	Symlink handling routines for the OSTA-UDF(tm) filesystem.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * COPYRIGHT
91da177e4SLinus Torvalds  *  (C) 1998-2001 Ben Fennema
101da177e4SLinus Torvalds  *  (C) 1999 Stelias Computing Inc
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * HISTORY
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  04/16/99 blf  Created.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  */
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include "udfdecl.h"
19e973606cSFabian Frederick #include <linux/uaccess.h>
201da177e4SLinus Torvalds #include <linux/errno.h>
211da177e4SLinus Torvalds #include <linux/fs.h>
221da177e4SLinus Torvalds #include <linux/time.h>
231da177e4SLinus Torvalds #include <linux/mm.h>
241da177e4SLinus Torvalds #include <linux/stat.h>
251da177e4SLinus Torvalds #include <linux/pagemap.h>
261da177e4SLinus Torvalds #include "udf_i.h"
271da177e4SLinus Torvalds 
udf_pc_to_char(struct super_block * sb,unsigned char * from,int fromlen,unsigned char * to,int tolen)280e5cc9a4SJan Kara static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
290e5cc9a4SJan Kara 			  int fromlen, unsigned char *to, int tolen)
301da177e4SLinus Torvalds {
311da177e4SLinus Torvalds 	struct pathComponent *pc;
321da177e4SLinus Torvalds 	int elen = 0;
330e5cc9a4SJan Kara 	int comp_len;
34391e8bbdSAl Viro 	unsigned char *p = to;
351da177e4SLinus Torvalds 
360e5cc9a4SJan Kara 	/* Reserve one byte for terminating \0 */
370e5cc9a4SJan Kara 	tolen--;
38cb00ea35SCyrill Gorcunov 	while (elen < fromlen) {
391da177e4SLinus Torvalds 		pc = (struct pathComponent *)(from + elen);
40e237ec37SJan Kara 		elen += sizeof(struct pathComponent);
41cb00ea35SCyrill Gorcunov 		switch (pc->componentType) {
421da177e4SLinus Torvalds 		case 1:
43fef2e9f3SJan Kara 			/*
44fef2e9f3SJan Kara 			 * Symlink points to some place which should be agreed
45fef2e9f3SJan Kara  			 * upon between originator and receiver of the media. Ignore.
46fef2e9f3SJan Kara 			 */
47e237ec37SJan Kara 			if (pc->lengthComponentIdent > 0) {
48e237ec37SJan Kara 				elen += pc->lengthComponentIdent;
49fef2e9f3SJan Kara 				break;
50e237ec37SJan Kara 			}
51df561f66SGustavo A. R. Silva 			fallthrough;
52fef2e9f3SJan Kara 		case 2:
530e5cc9a4SJan Kara 			if (tolen == 0)
540e5cc9a4SJan Kara 				return -ENAMETOOLONG;
551da177e4SLinus Torvalds 			p = to;
561da177e4SLinus Torvalds 			*p++ = '/';
570e5cc9a4SJan Kara 			tolen--;
581da177e4SLinus Torvalds 			break;
591da177e4SLinus Torvalds 		case 3:
600e5cc9a4SJan Kara 			if (tolen < 3)
610e5cc9a4SJan Kara 				return -ENAMETOOLONG;
621da177e4SLinus Torvalds 			memcpy(p, "../", 3);
631da177e4SLinus Torvalds 			p += 3;
640e5cc9a4SJan Kara 			tolen -= 3;
651da177e4SLinus Torvalds 			break;
661da177e4SLinus Torvalds 		case 4:
670e5cc9a4SJan Kara 			if (tolen < 2)
680e5cc9a4SJan Kara 				return -ENAMETOOLONG;
691da177e4SLinus Torvalds 			memcpy(p, "./", 2);
701da177e4SLinus Torvalds 			p += 2;
710e5cc9a4SJan Kara 			tolen -= 2;
721da177e4SLinus Torvalds 			/* that would be . - just ignore */
731da177e4SLinus Torvalds 			break;
741da177e4SLinus Torvalds 		case 5:
75e237ec37SJan Kara 			elen += pc->lengthComponentIdent;
76e237ec37SJan Kara 			if (elen > fromlen)
77e237ec37SJan Kara 				return -EIO;
780e5cc9a4SJan Kara 			comp_len = udf_get_filename(sb, pc->componentIdent,
790e5cc9a4SJan Kara 						    pc->lengthComponentIdent,
800e5cc9a4SJan Kara 						    p, tolen);
815ceb8b55SFabian Frederick 			if (comp_len < 0)
825ceb8b55SFabian Frederick 				return comp_len;
835ceb8b55SFabian Frederick 
840e5cc9a4SJan Kara 			p += comp_len;
850e5cc9a4SJan Kara 			tolen -= comp_len;
860e5cc9a4SJan Kara 			if (tolen == 0)
870e5cc9a4SJan Kara 				return -ENAMETOOLONG;
881da177e4SLinus Torvalds 			*p++ = '/';
890e5cc9a4SJan Kara 			tolen--;
901da177e4SLinus Torvalds 			break;
911da177e4SLinus Torvalds 		}
921da177e4SLinus Torvalds 	}
931da177e4SLinus Torvalds 	if (p > to + 1)
941da177e4SLinus Torvalds 		p[-1] = '\0';
951da177e4SLinus Torvalds 	else
961da177e4SLinus Torvalds 		p[0] = '\0';
970e5cc9a4SJan Kara 	return 0;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
udf_symlink_filler(struct file * file,struct folio * folio)1000c698cc5SMatthew Wilcox (Oracle) static int udf_symlink_filler(struct file *file, struct folio *folio)
1011da177e4SLinus Torvalds {
1020c698cc5SMatthew Wilcox (Oracle) 	struct page *page = &folio->page;
1031da177e4SLinus Torvalds 	struct inode *inode = page->mapping->host;
1041da177e4SLinus Torvalds 	struct buffer_head *bh = NULL;
105391e8bbdSAl Viro 	unsigned char *symlink;
10615a08f51SJan Kara 	int err = 0;
10721fc61c7SAl Viro 	unsigned char *p = page_address(page);
108f33321b2SJan Kara 	struct udf_inode_info *iinfo = UDF_I(inode);
1091da177e4SLinus Torvalds 
110a1d47b26SJan Kara 	/* We don't support symlinks longer than one block */
111a1d47b26SJan Kara 	if (inode->i_size > inode->i_sb->s_blocksize) {
112a1d47b26SJan Kara 		err = -ENAMETOOLONG;
113f33321b2SJan Kara 		goto out_unlock;
114a1d47b26SJan Kara 	}
115a1d47b26SJan Kara 
11648d6d8ffSMarcin Slusarz 	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
117382a2287SJan Kara 		symlink = iinfo->i_data + iinfo->i_lenEAttr;
11828de7948SCyrill Gorcunov 	} else {
11915a08f51SJan Kara 		bh = udf_bread(inode, 0, 0, &err);
120a1d47b26SJan Kara 		if (!bh) {
12115a08f51SJan Kara 			if (!err)
12215a08f51SJan Kara 				err = -EFSCORRUPTED;
123f33321b2SJan Kara 			goto out_err;
124a1d47b26SJan Kara 		}
1251da177e4SLinus Torvalds 		symlink = bh->b_data;
1261da177e4SLinus Torvalds 	}
1271da177e4SLinus Torvalds 
1280e5cc9a4SJan Kara 	err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
1293bf25cb4SJan Kara 	brelse(bh);
1300e5cc9a4SJan Kara 	if (err)
131f33321b2SJan Kara 		goto out_err;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	SetPageUptodate(page);
1341da177e4SLinus Torvalds 	unlock_page(page);
1351da177e4SLinus Torvalds 	return 0;
13628de7948SCyrill Gorcunov 
137f33321b2SJan Kara out_err:
1381da177e4SLinus Torvalds 	SetPageError(page);
139f33321b2SJan Kara out_unlock:
1401da177e4SLinus Torvalds 	unlock_page(page);
1411da177e4SLinus Torvalds 	return err;
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds 
udf_symlink_getattr(struct mnt_idmap * idmap,const struct path * path,struct kstat * stat,u32 request_mask,unsigned int flags)144b74d24f7SChristian Brauner static int udf_symlink_getattr(struct mnt_idmap *idmap,
145549c7297SChristian Brauner 			       const struct path *path, struct kstat *stat,
146a528d35eSDavid Howells 			       u32 request_mask, unsigned int flags)
147ad4d0532SJan Kara {
148a528d35eSDavid Howells 	struct dentry *dentry = path->dentry;
149ad4d0532SJan Kara 	struct inode *inode = d_backing_inode(dentry);
150ad4d0532SJan Kara 	struct page *page;
151ad4d0532SJan Kara 
152*0d72b928SJeff Layton 	generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
153ad4d0532SJan Kara 	page = read_mapping_page(inode->i_mapping, 0, NULL);
154ad4d0532SJan Kara 	if (IS_ERR(page))
155ad4d0532SJan Kara 		return PTR_ERR(page);
156ad4d0532SJan Kara 	/*
157ad4d0532SJan Kara 	 * UDF uses non-trivial encoding of symlinks so i_size does not match
158ad4d0532SJan Kara 	 * number of characters reported by readlink(2) which apparently some
159ad4d0532SJan Kara 	 * applications expect. Also POSIX says that "The value returned in the
160ad4d0532SJan Kara 	 * st_size field shall be the length of the contents of the symbolic
161ad4d0532SJan Kara 	 * link, and shall not count a trailing null if one is present." So
162ad4d0532SJan Kara 	 * let's report the length of string returned by readlink(2) for
163ad4d0532SJan Kara 	 * st_size.
164ad4d0532SJan Kara 	 */
165ad4d0532SJan Kara 	stat->size = strlen(page_address(page));
166ad4d0532SJan Kara 	put_page(page);
167ad4d0532SJan Kara 
168ad4d0532SJan Kara 	return 0;
169ad4d0532SJan Kara }
170ad4d0532SJan Kara 
1711da177e4SLinus Torvalds /*
1721da177e4SLinus Torvalds  * symlinks can't do much...
1731da177e4SLinus Torvalds  */
174f5e54d6eSChristoph Hellwig const struct address_space_operations udf_symlink_aops = {
1750c698cc5SMatthew Wilcox (Oracle) 	.read_folio		= udf_symlink_filler,
1761da177e4SLinus Torvalds };
177ad4d0532SJan Kara 
178ad4d0532SJan Kara const struct inode_operations udf_symlink_inode_operations = {
179ad4d0532SJan Kara 	.get_link	= page_get_link,
180ad4d0532SJan Kara 	.getattr	= udf_symlink_getattr,
181ad4d0532SJan Kara };
182