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 if (comp_len < 0) 86 return comp_len; 87 88 p += comp_len; 89 tolen -= comp_len; 90 if (tolen == 0) 91 return -ENAMETOOLONG; 92 *p++ = '/'; 93 tolen--; 94 break; 95 } 96 } 97 if (p > to + 1) 98 p[-1] = '\0'; 99 else 100 p[0] = '\0'; 101 return 0; 102 } 103 104 static int udf_symlink_filler(struct file *file, struct page *page) 105 { 106 struct inode *inode = page->mapping->host; 107 struct buffer_head *bh = NULL; 108 unsigned char *symlink; 109 int err; 110 unsigned char *p = kmap(page); 111 struct udf_inode_info *iinfo; 112 uint32_t pos; 113 114 /* We don't support symlinks longer than one block */ 115 if (inode->i_size > inode->i_sb->s_blocksize) { 116 err = -ENAMETOOLONG; 117 goto out_unmap; 118 } 119 120 iinfo = UDF_I(inode); 121 pos = udf_block_map(inode, 0); 122 123 down_read(&iinfo->i_data_sem); 124 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 125 symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr; 126 } else { 127 bh = sb_bread(inode->i_sb, pos); 128 129 if (!bh) { 130 err = -EIO; 131 goto out_unlock_inode; 132 } 133 134 symlink = bh->b_data; 135 } 136 137 err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE); 138 brelse(bh); 139 if (err) 140 goto out_unlock_inode; 141 142 up_read(&iinfo->i_data_sem); 143 SetPageUptodate(page); 144 kunmap(page); 145 unlock_page(page); 146 return 0; 147 148 out_unlock_inode: 149 up_read(&iinfo->i_data_sem); 150 SetPageError(page); 151 out_unmap: 152 kunmap(page); 153 unlock_page(page); 154 return err; 155 } 156 157 /* 158 * symlinks can't do much... 159 */ 160 const struct address_space_operations udf_symlink_aops = { 161 .readpage = udf_symlink_filler, 162 }; 163