1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * symlink.c
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * PURPOSE
5*4882a593Smuzhiyun * Symlink handling routines for the OSTA-UDF(tm) filesystem.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * COPYRIGHT
8*4882a593Smuzhiyun * This file is distributed under the terms of the GNU General Public
9*4882a593Smuzhiyun * License (GPL). Copies of the GPL can be obtained from:
10*4882a593Smuzhiyun * ftp://prep.ai.mit.edu/pub/gnu/GPL
11*4882a593Smuzhiyun * Each contributing author retains all rights to their own work.
12*4882a593Smuzhiyun *
13*4882a593Smuzhiyun * (C) 1998-2001 Ben Fennema
14*4882a593Smuzhiyun * (C) 1999 Stelias Computing Inc
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * HISTORY
17*4882a593Smuzhiyun *
18*4882a593Smuzhiyun * 04/16/99 blf Created.
19*4882a593Smuzhiyun *
20*4882a593Smuzhiyun */
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include "udfdecl.h"
23*4882a593Smuzhiyun #include <linux/uaccess.h>
24*4882a593Smuzhiyun #include <linux/errno.h>
25*4882a593Smuzhiyun #include <linux/fs.h>
26*4882a593Smuzhiyun #include <linux/time.h>
27*4882a593Smuzhiyun #include <linux/mm.h>
28*4882a593Smuzhiyun #include <linux/stat.h>
29*4882a593Smuzhiyun #include <linux/pagemap.h>
30*4882a593Smuzhiyun #include "udf_i.h"
31*4882a593Smuzhiyun
udf_pc_to_char(struct super_block * sb,unsigned char * from,int fromlen,unsigned char * to,int tolen)32*4882a593Smuzhiyun static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
33*4882a593Smuzhiyun int fromlen, unsigned char *to, int tolen)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun struct pathComponent *pc;
36*4882a593Smuzhiyun int elen = 0;
37*4882a593Smuzhiyun int comp_len;
38*4882a593Smuzhiyun unsigned char *p = to;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun /* Reserve one byte for terminating \0 */
41*4882a593Smuzhiyun tolen--;
42*4882a593Smuzhiyun while (elen < fromlen) {
43*4882a593Smuzhiyun pc = (struct pathComponent *)(from + elen);
44*4882a593Smuzhiyun elen += sizeof(struct pathComponent);
45*4882a593Smuzhiyun switch (pc->componentType) {
46*4882a593Smuzhiyun case 1:
47*4882a593Smuzhiyun /*
48*4882a593Smuzhiyun * Symlink points to some place which should be agreed
49*4882a593Smuzhiyun * upon between originator and receiver of the media. Ignore.
50*4882a593Smuzhiyun */
51*4882a593Smuzhiyun if (pc->lengthComponentIdent > 0) {
52*4882a593Smuzhiyun elen += pc->lengthComponentIdent;
53*4882a593Smuzhiyun break;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun fallthrough;
56*4882a593Smuzhiyun case 2:
57*4882a593Smuzhiyun if (tolen == 0)
58*4882a593Smuzhiyun return -ENAMETOOLONG;
59*4882a593Smuzhiyun p = to;
60*4882a593Smuzhiyun *p++ = '/';
61*4882a593Smuzhiyun tolen--;
62*4882a593Smuzhiyun break;
63*4882a593Smuzhiyun case 3:
64*4882a593Smuzhiyun if (tolen < 3)
65*4882a593Smuzhiyun return -ENAMETOOLONG;
66*4882a593Smuzhiyun memcpy(p, "../", 3);
67*4882a593Smuzhiyun p += 3;
68*4882a593Smuzhiyun tolen -= 3;
69*4882a593Smuzhiyun break;
70*4882a593Smuzhiyun case 4:
71*4882a593Smuzhiyun if (tolen < 2)
72*4882a593Smuzhiyun return -ENAMETOOLONG;
73*4882a593Smuzhiyun memcpy(p, "./", 2);
74*4882a593Smuzhiyun p += 2;
75*4882a593Smuzhiyun tolen -= 2;
76*4882a593Smuzhiyun /* that would be . - just ignore */
77*4882a593Smuzhiyun break;
78*4882a593Smuzhiyun case 5:
79*4882a593Smuzhiyun elen += pc->lengthComponentIdent;
80*4882a593Smuzhiyun if (elen > fromlen)
81*4882a593Smuzhiyun return -EIO;
82*4882a593Smuzhiyun comp_len = udf_get_filename(sb, pc->componentIdent,
83*4882a593Smuzhiyun pc->lengthComponentIdent,
84*4882a593Smuzhiyun p, tolen);
85*4882a593Smuzhiyun if (comp_len < 0)
86*4882a593Smuzhiyun return comp_len;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun p += comp_len;
89*4882a593Smuzhiyun tolen -= comp_len;
90*4882a593Smuzhiyun if (tolen == 0)
91*4882a593Smuzhiyun return -ENAMETOOLONG;
92*4882a593Smuzhiyun *p++ = '/';
93*4882a593Smuzhiyun tolen--;
94*4882a593Smuzhiyun break;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun if (p > to + 1)
98*4882a593Smuzhiyun p[-1] = '\0';
99*4882a593Smuzhiyun else
100*4882a593Smuzhiyun p[0] = '\0';
101*4882a593Smuzhiyun return 0;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
udf_symlink_filler(struct file * file,struct page * page)104*4882a593Smuzhiyun static int udf_symlink_filler(struct file *file, struct page *page)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun struct inode *inode = page->mapping->host;
107*4882a593Smuzhiyun struct buffer_head *bh = NULL;
108*4882a593Smuzhiyun unsigned char *symlink;
109*4882a593Smuzhiyun int err;
110*4882a593Smuzhiyun unsigned char *p = page_address(page);
111*4882a593Smuzhiyun struct udf_inode_info *iinfo;
112*4882a593Smuzhiyun uint32_t pos;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* We don't support symlinks longer than one block */
115*4882a593Smuzhiyun if (inode->i_size > inode->i_sb->s_blocksize) {
116*4882a593Smuzhiyun err = -ENAMETOOLONG;
117*4882a593Smuzhiyun goto out_unmap;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun iinfo = UDF_I(inode);
121*4882a593Smuzhiyun pos = udf_block_map(inode, 0);
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun down_read(&iinfo->i_data_sem);
124*4882a593Smuzhiyun if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
125*4882a593Smuzhiyun symlink = iinfo->i_data + iinfo->i_lenEAttr;
126*4882a593Smuzhiyun } else {
127*4882a593Smuzhiyun bh = sb_bread(inode->i_sb, pos);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun if (!bh) {
130*4882a593Smuzhiyun err = -EIO;
131*4882a593Smuzhiyun goto out_unlock_inode;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun symlink = bh->b_data;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
138*4882a593Smuzhiyun brelse(bh);
139*4882a593Smuzhiyun if (err)
140*4882a593Smuzhiyun goto out_unlock_inode;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun up_read(&iinfo->i_data_sem);
143*4882a593Smuzhiyun SetPageUptodate(page);
144*4882a593Smuzhiyun unlock_page(page);
145*4882a593Smuzhiyun return 0;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun out_unlock_inode:
148*4882a593Smuzhiyun up_read(&iinfo->i_data_sem);
149*4882a593Smuzhiyun SetPageError(page);
150*4882a593Smuzhiyun out_unmap:
151*4882a593Smuzhiyun unlock_page(page);
152*4882a593Smuzhiyun return err;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun
udf_symlink_getattr(const struct path * path,struct kstat * stat,u32 request_mask,unsigned int flags)155*4882a593Smuzhiyun static int udf_symlink_getattr(const struct path *path, struct kstat *stat,
156*4882a593Smuzhiyun u32 request_mask, unsigned int flags)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct dentry *dentry = path->dentry;
159*4882a593Smuzhiyun struct inode *inode = d_backing_inode(dentry);
160*4882a593Smuzhiyun struct page *page;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun generic_fillattr(inode, stat);
163*4882a593Smuzhiyun page = read_mapping_page(inode->i_mapping, 0, NULL);
164*4882a593Smuzhiyun if (IS_ERR(page))
165*4882a593Smuzhiyun return PTR_ERR(page);
166*4882a593Smuzhiyun /*
167*4882a593Smuzhiyun * UDF uses non-trivial encoding of symlinks so i_size does not match
168*4882a593Smuzhiyun * number of characters reported by readlink(2) which apparently some
169*4882a593Smuzhiyun * applications expect. Also POSIX says that "The value returned in the
170*4882a593Smuzhiyun * st_size field shall be the length of the contents of the symbolic
171*4882a593Smuzhiyun * link, and shall not count a trailing null if one is present." So
172*4882a593Smuzhiyun * let's report the length of string returned by readlink(2) for
173*4882a593Smuzhiyun * st_size.
174*4882a593Smuzhiyun */
175*4882a593Smuzhiyun stat->size = strlen(page_address(page));
176*4882a593Smuzhiyun put_page(page);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun return 0;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun /*
182*4882a593Smuzhiyun * symlinks can't do much...
183*4882a593Smuzhiyun */
184*4882a593Smuzhiyun const struct address_space_operations udf_symlink_aops = {
185*4882a593Smuzhiyun .readpage = udf_symlink_filler,
186*4882a593Smuzhiyun };
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun const struct inode_operations udf_symlink_inode_operations = {
189*4882a593Smuzhiyun .get_link = page_get_link,
190*4882a593Smuzhiyun .getattr = udf_symlink_getattr,
191*4882a593Smuzhiyun };
192