18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * symlink.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * PURPOSE
58c2ecf20Sopenharmony_ci *	Symlink handling routines for the OSTA-UDF(tm) filesystem.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * COPYRIGHT
88c2ecf20Sopenharmony_ci *	This file is distributed under the terms of the GNU General Public
98c2ecf20Sopenharmony_ci *	License (GPL). Copies of the GPL can be obtained from:
108c2ecf20Sopenharmony_ci *		ftp://prep.ai.mit.edu/pub/gnu/GPL
118c2ecf20Sopenharmony_ci *	Each contributing author retains all rights to their own work.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *  (C) 1998-2001 Ben Fennema
148c2ecf20Sopenharmony_ci *  (C) 1999 Stelias Computing Inc
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * HISTORY
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *  04/16/99 blf  Created.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "udfdecl.h"
238c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
248c2ecf20Sopenharmony_ci#include <linux/errno.h>
258c2ecf20Sopenharmony_ci#include <linux/fs.h>
268c2ecf20Sopenharmony_ci#include <linux/time.h>
278c2ecf20Sopenharmony_ci#include <linux/mm.h>
288c2ecf20Sopenharmony_ci#include <linux/stat.h>
298c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
308c2ecf20Sopenharmony_ci#include "udf_i.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int udf_pc_to_char(struct super_block *sb, unsigned char *from,
338c2ecf20Sopenharmony_ci			  int fromlen, unsigned char *to, int tolen)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct pathComponent *pc;
368c2ecf20Sopenharmony_ci	int elen = 0;
378c2ecf20Sopenharmony_ci	int comp_len;
388c2ecf20Sopenharmony_ci	unsigned char *p = to;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/* Reserve one byte for terminating \0 */
418c2ecf20Sopenharmony_ci	tolen--;
428c2ecf20Sopenharmony_ci	while (elen < fromlen) {
438c2ecf20Sopenharmony_ci		pc = (struct pathComponent *)(from + elen);
448c2ecf20Sopenharmony_ci		elen += sizeof(struct pathComponent);
458c2ecf20Sopenharmony_ci		switch (pc->componentType) {
468c2ecf20Sopenharmony_ci		case 1:
478c2ecf20Sopenharmony_ci			/*
488c2ecf20Sopenharmony_ci			 * Symlink points to some place which should be agreed
498c2ecf20Sopenharmony_ci 			 * upon between originator and receiver of the media. Ignore.
508c2ecf20Sopenharmony_ci			 */
518c2ecf20Sopenharmony_ci			if (pc->lengthComponentIdent > 0) {
528c2ecf20Sopenharmony_ci				elen += pc->lengthComponentIdent;
538c2ecf20Sopenharmony_ci				break;
548c2ecf20Sopenharmony_ci			}
558c2ecf20Sopenharmony_ci			fallthrough;
568c2ecf20Sopenharmony_ci		case 2:
578c2ecf20Sopenharmony_ci			if (tolen == 0)
588c2ecf20Sopenharmony_ci				return -ENAMETOOLONG;
598c2ecf20Sopenharmony_ci			p = to;
608c2ecf20Sopenharmony_ci			*p++ = '/';
618c2ecf20Sopenharmony_ci			tolen--;
628c2ecf20Sopenharmony_ci			break;
638c2ecf20Sopenharmony_ci		case 3:
648c2ecf20Sopenharmony_ci			if (tolen < 3)
658c2ecf20Sopenharmony_ci				return -ENAMETOOLONG;
668c2ecf20Sopenharmony_ci			memcpy(p, "../", 3);
678c2ecf20Sopenharmony_ci			p += 3;
688c2ecf20Sopenharmony_ci			tolen -= 3;
698c2ecf20Sopenharmony_ci			break;
708c2ecf20Sopenharmony_ci		case 4:
718c2ecf20Sopenharmony_ci			if (tolen < 2)
728c2ecf20Sopenharmony_ci				return -ENAMETOOLONG;
738c2ecf20Sopenharmony_ci			memcpy(p, "./", 2);
748c2ecf20Sopenharmony_ci			p += 2;
758c2ecf20Sopenharmony_ci			tolen -= 2;
768c2ecf20Sopenharmony_ci			/* that would be . - just ignore */
778c2ecf20Sopenharmony_ci			break;
788c2ecf20Sopenharmony_ci		case 5:
798c2ecf20Sopenharmony_ci			elen += pc->lengthComponentIdent;
808c2ecf20Sopenharmony_ci			if (elen > fromlen)
818c2ecf20Sopenharmony_ci				return -EIO;
828c2ecf20Sopenharmony_ci			comp_len = udf_get_filename(sb, pc->componentIdent,
838c2ecf20Sopenharmony_ci						    pc->lengthComponentIdent,
848c2ecf20Sopenharmony_ci						    p, tolen);
858c2ecf20Sopenharmony_ci			if (comp_len < 0)
868c2ecf20Sopenharmony_ci				return comp_len;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci			p += comp_len;
898c2ecf20Sopenharmony_ci			tolen -= comp_len;
908c2ecf20Sopenharmony_ci			if (tolen == 0)
918c2ecf20Sopenharmony_ci				return -ENAMETOOLONG;
928c2ecf20Sopenharmony_ci			*p++ = '/';
938c2ecf20Sopenharmony_ci			tolen--;
948c2ecf20Sopenharmony_ci			break;
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci	if (p > to + 1)
988c2ecf20Sopenharmony_ci		p[-1] = '\0';
998c2ecf20Sopenharmony_ci	else
1008c2ecf20Sopenharmony_ci		p[0] = '\0';
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int udf_symlink_filler(struct file *file, struct page *page)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct inode *inode = page->mapping->host;
1078c2ecf20Sopenharmony_ci	struct buffer_head *bh = NULL;
1088c2ecf20Sopenharmony_ci	unsigned char *symlink;
1098c2ecf20Sopenharmony_ci	int err;
1108c2ecf20Sopenharmony_ci	unsigned char *p = page_address(page);
1118c2ecf20Sopenharmony_ci	struct udf_inode_info *iinfo;
1128c2ecf20Sopenharmony_ci	uint32_t pos;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* We don't support symlinks longer than one block */
1158c2ecf20Sopenharmony_ci	if (inode->i_size > inode->i_sb->s_blocksize) {
1168c2ecf20Sopenharmony_ci		err = -ENAMETOOLONG;
1178c2ecf20Sopenharmony_ci		goto out_unmap;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	iinfo = UDF_I(inode);
1218c2ecf20Sopenharmony_ci	pos = udf_block_map(inode, 0);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	down_read(&iinfo->i_data_sem);
1248c2ecf20Sopenharmony_ci	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
1258c2ecf20Sopenharmony_ci		symlink = iinfo->i_data + iinfo->i_lenEAttr;
1268c2ecf20Sopenharmony_ci	} else {
1278c2ecf20Sopenharmony_ci		bh = sb_bread(inode->i_sb, pos);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		if (!bh) {
1308c2ecf20Sopenharmony_ci			err = -EIO;
1318c2ecf20Sopenharmony_ci			goto out_unlock_inode;
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		symlink = bh->b_data;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
1388c2ecf20Sopenharmony_ci	brelse(bh);
1398c2ecf20Sopenharmony_ci	if (err)
1408c2ecf20Sopenharmony_ci		goto out_unlock_inode;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	up_read(&iinfo->i_data_sem);
1438c2ecf20Sopenharmony_ci	SetPageUptodate(page);
1448c2ecf20Sopenharmony_ci	unlock_page(page);
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ciout_unlock_inode:
1488c2ecf20Sopenharmony_ci	up_read(&iinfo->i_data_sem);
1498c2ecf20Sopenharmony_ci	SetPageError(page);
1508c2ecf20Sopenharmony_ciout_unmap:
1518c2ecf20Sopenharmony_ci	unlock_page(page);
1528c2ecf20Sopenharmony_ci	return err;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int udf_symlink_getattr(const struct path *path, struct kstat *stat,
1568c2ecf20Sopenharmony_ci				u32 request_mask, unsigned int flags)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct dentry *dentry = path->dentry;
1598c2ecf20Sopenharmony_ci	struct inode *inode = d_backing_inode(dentry);
1608c2ecf20Sopenharmony_ci	struct page *page;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	generic_fillattr(inode, stat);
1638c2ecf20Sopenharmony_ci	page = read_mapping_page(inode->i_mapping, 0, NULL);
1648c2ecf20Sopenharmony_ci	if (IS_ERR(page))
1658c2ecf20Sopenharmony_ci		return PTR_ERR(page);
1668c2ecf20Sopenharmony_ci	/*
1678c2ecf20Sopenharmony_ci	 * UDF uses non-trivial encoding of symlinks so i_size does not match
1688c2ecf20Sopenharmony_ci	 * number of characters reported by readlink(2) which apparently some
1698c2ecf20Sopenharmony_ci	 * applications expect. Also POSIX says that "The value returned in the
1708c2ecf20Sopenharmony_ci	 * st_size field shall be the length of the contents of the symbolic
1718c2ecf20Sopenharmony_ci	 * link, and shall not count a trailing null if one is present." So
1728c2ecf20Sopenharmony_ci	 * let's report the length of string returned by readlink(2) for
1738c2ecf20Sopenharmony_ci	 * st_size.
1748c2ecf20Sopenharmony_ci	 */
1758c2ecf20Sopenharmony_ci	stat->size = strlen(page_address(page));
1768c2ecf20Sopenharmony_ci	put_page(page);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/*
1828c2ecf20Sopenharmony_ci * symlinks can't do much...
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_ciconst struct address_space_operations udf_symlink_aops = {
1858c2ecf20Sopenharmony_ci	.readpage		= udf_symlink_filler,
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ciconst struct inode_operations udf_symlink_inode_operations = {
1898c2ecf20Sopenharmony_ci	.get_link	= page_get_link,
1908c2ecf20Sopenharmony_ci	.getattr	= udf_symlink_getattr,
1918c2ecf20Sopenharmony_ci};
192