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