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 <linux/buffer_head.h> 31 #include "udf_i.h" 32 33 static int udf_pc_to_char(struct super_block *sb, unsigned char *from, 34 int fromlen, unsigned char *to, int tolen) 35 { 36 struct pathComponent *pc; 37 int elen = 0; 38 int comp_len; 39 unsigned char *p = to; 40 41 /* Reserve one byte for terminating \0 */ 42 tolen--; 43 while (elen < fromlen) { 44 pc = (struct pathComponent *)(from + elen); 45 elen += sizeof(struct pathComponent); 46 switch (pc->componentType) { 47 case 1: 48 /* 49 * Symlink points to some place which should be agreed 50 * upon between originator and receiver of the media. Ignore. 51 */ 52 if (pc->lengthComponentIdent > 0) { 53 elen += pc->lengthComponentIdent; 54 break; 55 } 56 /* Fall through */ 57 case 2: 58 if (tolen == 0) 59 return -ENAMETOOLONG; 60 p = to; 61 *p++ = '/'; 62 tolen--; 63 break; 64 case 3: 65 if (tolen < 3) 66 return -ENAMETOOLONG; 67 memcpy(p, "../", 3); 68 p += 3; 69 tolen -= 3; 70 break; 71 case 4: 72 if (tolen < 2) 73 return -ENAMETOOLONG; 74 memcpy(p, "./", 2); 75 p += 2; 76 tolen -= 2; 77 /* that would be . - just ignore */ 78 break; 79 case 5: 80 elen += pc->lengthComponentIdent; 81 if (elen > fromlen) 82 return -EIO; 83 comp_len = udf_get_filename(sb, pc->componentIdent, 84 pc->lengthComponentIdent, 85 p, tolen); 86 p += comp_len; 87 tolen -= comp_len; 88 if (tolen == 0) 89 return -ENAMETOOLONG; 90 *p++ = '/'; 91 tolen--; 92 break; 93 } 94 } 95 if (p > to + 1) 96 p[-1] = '\0'; 97 else 98 p[0] = '\0'; 99 return 0; 100 } 101 102 static int udf_symlink_filler(struct file *file, struct page *page) 103 { 104 struct inode *inode = page->mapping->host; 105 struct buffer_head *bh = NULL; 106 unsigned char *symlink; 107 int err; 108 unsigned char *p = kmap(page); 109 struct udf_inode_info *iinfo; 110 uint32_t pos; 111 112 /* We don't support symlinks longer than one block */ 113 if (inode->i_size > inode->i_sb->s_blocksize) { 114 err = -ENAMETOOLONG; 115 goto out_unmap; 116 } 117 118 iinfo = UDF_I(inode); 119 pos = udf_block_map(inode, 0); 120 121 down_read(&iinfo->i_data_sem); 122 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 123 symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr; 124 } else { 125 bh = sb_bread(inode->i_sb, pos); 126 127 if (!bh) { 128 err = -EIO; 129 goto out_unlock_inode; 130 } 131 132 symlink = bh->b_data; 133 } 134 135 err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE); 136 brelse(bh); 137 if (err) 138 goto out_unlock_inode; 139 140 up_read(&iinfo->i_data_sem); 141 SetPageUptodate(page); 142 kunmap(page); 143 unlock_page(page); 144 return 0; 145 146 out_unlock_inode: 147 up_read(&iinfo->i_data_sem); 148 SetPageError(page); 149 out_unmap: 150 kunmap(page); 151 unlock_page(page); 152 return err; 153 } 154 155 /* 156 * symlinks can't do much... 157 */ 158 const struct address_space_operations udf_symlink_aops = { 159 .readpage = udf_symlink_filler, 160 }; 161