1 /* 2 * symlink.c 3 * 4 * PURPOSE 5 * Symlink handling routines for the OSTA-UDF(tm) filesystem. 6 * 7 * COPYRIGHT 8 * This file is distributed under the terms of the GNU General Public 9 * License (GPL). Copies of the GPL can be obtained from: 10 * ftp://prep.ai.mit.edu/pub/gnu/GPL 11 * Each contributing author retains all rights to their own work. 12 * 13 * (C) 1998-2001 Ben Fennema 14 * (C) 1999 Stelias Computing Inc 15 * 16 * HISTORY 17 * 18 * 04/16/99 blf Created. 19 * 20 */ 21 22 #include "udfdecl.h" 23 #include <linux/uaccess.h> 24 #include <linux/errno.h> 25 #include <linux/fs.h> 26 #include <linux/time.h> 27 #include <linux/mm.h> 28 #include <linux/stat.h> 29 #include <linux/pagemap.h> 30 #include "udf_i.h" 31 32 static int udf_pc_to_char(struct super_block *sb, unsigned char *from, 33 int fromlen, unsigned char *to, int tolen) 34 { 35 struct pathComponent *pc; 36 int elen = 0; 37 int comp_len; 38 unsigned char *p = to; 39 40 /* Reserve one byte for terminating \0 */ 41 tolen--; 42 while (elen < fromlen) { 43 pc = (struct pathComponent *)(from + elen); 44 elen += sizeof(struct pathComponent); 45 switch (pc->componentType) { 46 case 1: 47 /* 48 * Symlink points to some place which should be agreed 49 * upon between originator and receiver of the media. Ignore. 50 */ 51 if (pc->lengthComponentIdent > 0) { 52 elen += pc->lengthComponentIdent; 53 break; 54 } 55 /* Fall through */ 56 case 2: 57 if (tolen == 0) 58 return -ENAMETOOLONG; 59 p = to; 60 *p++ = '/'; 61 tolen--; 62 break; 63 case 3: 64 if (tolen < 3) 65 return -ENAMETOOLONG; 66 memcpy(p, "../", 3); 67 p += 3; 68 tolen -= 3; 69 break; 70 case 4: 71 if (tolen < 2) 72 return -ENAMETOOLONG; 73 memcpy(p, "./", 2); 74 p += 2; 75 tolen -= 2; 76 /* that would be . - just ignore */ 77 break; 78 case 5: 79 elen += pc->lengthComponentIdent; 80 if (elen > fromlen) 81 return -EIO; 82 comp_len = udf_get_filename(sb, pc->componentIdent, 83 pc->lengthComponentIdent, 84 p, tolen); 85 p += comp_len; 86 tolen -= comp_len; 87 if (tolen == 0) 88 return -ENAMETOOLONG; 89 *p++ = '/'; 90 tolen--; 91 break; 92 } 93 } 94 if (p > to + 1) 95 p[-1] = '\0'; 96 else 97 p[0] = '\0'; 98 return 0; 99 } 100 101 static int udf_symlink_filler(struct file *file, struct page *page) 102 { 103 struct inode *inode = page->mapping->host; 104 struct buffer_head *bh = NULL; 105 unsigned char *symlink; 106 int err; 107 unsigned char *p = kmap(page); 108 struct udf_inode_info *iinfo; 109 uint32_t pos; 110 111 /* We don't support symlinks longer than one block */ 112 if (inode->i_size > inode->i_sb->s_blocksize) { 113 err = -ENAMETOOLONG; 114 goto out_unmap; 115 } 116 117 iinfo = UDF_I(inode); 118 pos = udf_block_map(inode, 0); 119 120 down_read(&iinfo->i_data_sem); 121 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 122 symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr; 123 } else { 124 bh = sb_bread(inode->i_sb, pos); 125 126 if (!bh) { 127 err = -EIO; 128 goto out_unlock_inode; 129 } 130 131 symlink = bh->b_data; 132 } 133 134 err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE); 135 brelse(bh); 136 if (err) 137 goto out_unlock_inode; 138 139 up_read(&iinfo->i_data_sem); 140 SetPageUptodate(page); 141 kunmap(page); 142 unlock_page(page); 143 return 0; 144 145 out_unlock_inode: 146 up_read(&iinfo->i_data_sem); 147 SetPageError(page); 148 out_unmap: 149 kunmap(page); 150 unlock_page(page); 151 return err; 152 } 153 154 /* 155 * symlinks can't do much... 156 */ 157 const struct address_space_operations udf_symlink_aops = { 158 .readpage = udf_symlink_filler, 159 }; 160