15ce34554SBagas Sanjaya // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * symlink.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * PURPOSE
61da177e4SLinus Torvalds * Symlink handling routines for the OSTA-UDF(tm) filesystem.
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * COPYRIGHT
91da177e4SLinus Torvalds * (C) 1998-2001 Ben Fennema
101da177e4SLinus Torvalds * (C) 1999 Stelias Computing Inc
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds * HISTORY
131da177e4SLinus Torvalds *
141da177e4SLinus Torvalds * 04/16/99 blf Created.
151da177e4SLinus Torvalds *
161da177e4SLinus Torvalds */
171da177e4SLinus Torvalds
181da177e4SLinus Torvalds #include "udfdecl.h"
19e973606cSFabian Frederick #include <linux/uaccess.h>
201da177e4SLinus Torvalds #include <linux/errno.h>
211da177e4SLinus Torvalds #include <linux/fs.h>
221da177e4SLinus Torvalds #include <linux/time.h>
231da177e4SLinus Torvalds #include <linux/mm.h>
241da177e4SLinus Torvalds #include <linux/stat.h>
251da177e4SLinus Torvalds #include <linux/pagemap.h>
261da177e4SLinus Torvalds #include "udf_i.h"
271da177e4SLinus Torvalds
udf_pc_to_char(struct super_block * sb,unsigned char * from,int fromlen,unsigned char * to,int tolen)280e5cc9a4SJan Kara static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
290e5cc9a4SJan Kara int fromlen, unsigned char *to, int tolen)
301da177e4SLinus Torvalds {
311da177e4SLinus Torvalds struct pathComponent *pc;
321da177e4SLinus Torvalds int elen = 0;
330e5cc9a4SJan Kara int comp_len;
34391e8bbdSAl Viro unsigned char *p = to;
351da177e4SLinus Torvalds
360e5cc9a4SJan Kara /* Reserve one byte for terminating \0 */
370e5cc9a4SJan Kara tolen--;
38cb00ea35SCyrill Gorcunov while (elen < fromlen) {
391da177e4SLinus Torvalds pc = (struct pathComponent *)(from + elen);
40e237ec37SJan Kara elen += sizeof(struct pathComponent);
41cb00ea35SCyrill Gorcunov switch (pc->componentType) {
421da177e4SLinus Torvalds case 1:
43fef2e9f3SJan Kara /*
44fef2e9f3SJan Kara * Symlink points to some place which should be agreed
45fef2e9f3SJan Kara * upon between originator and receiver of the media. Ignore.
46fef2e9f3SJan Kara */
47e237ec37SJan Kara if (pc->lengthComponentIdent > 0) {
48e237ec37SJan Kara elen += pc->lengthComponentIdent;
49fef2e9f3SJan Kara break;
50e237ec37SJan Kara }
51df561f66SGustavo A. R. Silva fallthrough;
52fef2e9f3SJan Kara case 2:
530e5cc9a4SJan Kara if (tolen == 0)
540e5cc9a4SJan Kara return -ENAMETOOLONG;
551da177e4SLinus Torvalds p = to;
561da177e4SLinus Torvalds *p++ = '/';
570e5cc9a4SJan Kara tolen--;
581da177e4SLinus Torvalds break;
591da177e4SLinus Torvalds case 3:
600e5cc9a4SJan Kara if (tolen < 3)
610e5cc9a4SJan Kara return -ENAMETOOLONG;
621da177e4SLinus Torvalds memcpy(p, "../", 3);
631da177e4SLinus Torvalds p += 3;
640e5cc9a4SJan Kara tolen -= 3;
651da177e4SLinus Torvalds break;
661da177e4SLinus Torvalds case 4:
670e5cc9a4SJan Kara if (tolen < 2)
680e5cc9a4SJan Kara return -ENAMETOOLONG;
691da177e4SLinus Torvalds memcpy(p, "./", 2);
701da177e4SLinus Torvalds p += 2;
710e5cc9a4SJan Kara tolen -= 2;
721da177e4SLinus Torvalds /* that would be . - just ignore */
731da177e4SLinus Torvalds break;
741da177e4SLinus Torvalds case 5:
75e237ec37SJan Kara elen += pc->lengthComponentIdent;
76e237ec37SJan Kara if (elen > fromlen)
77e237ec37SJan Kara return -EIO;
780e5cc9a4SJan Kara comp_len = udf_get_filename(sb, pc->componentIdent,
790e5cc9a4SJan Kara pc->lengthComponentIdent,
800e5cc9a4SJan Kara p, tolen);
815ceb8b55SFabian Frederick if (comp_len < 0)
825ceb8b55SFabian Frederick return comp_len;
835ceb8b55SFabian Frederick
840e5cc9a4SJan Kara p += comp_len;
850e5cc9a4SJan Kara tolen -= comp_len;
860e5cc9a4SJan Kara if (tolen == 0)
870e5cc9a4SJan Kara return -ENAMETOOLONG;
881da177e4SLinus Torvalds *p++ = '/';
890e5cc9a4SJan Kara tolen--;
901da177e4SLinus Torvalds break;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds if (p > to + 1)
941da177e4SLinus Torvalds p[-1] = '\0';
951da177e4SLinus Torvalds else
961da177e4SLinus Torvalds p[0] = '\0';
970e5cc9a4SJan Kara return 0;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds
udf_symlink_filler(struct file * file,struct folio * folio)1000c698cc5SMatthew Wilcox (Oracle) static int udf_symlink_filler(struct file *file, struct folio *folio)
1011da177e4SLinus Torvalds {
1020c698cc5SMatthew Wilcox (Oracle) struct page *page = &folio->page;
1031da177e4SLinus Torvalds struct inode *inode = page->mapping->host;
1041da177e4SLinus Torvalds struct buffer_head *bh = NULL;
105391e8bbdSAl Viro unsigned char *symlink;
10615a08f51SJan Kara int err = 0;
10721fc61c7SAl Viro unsigned char *p = page_address(page);
108f33321b2SJan Kara struct udf_inode_info *iinfo = UDF_I(inode);
1091da177e4SLinus Torvalds
110a1d47b26SJan Kara /* We don't support symlinks longer than one block */
111a1d47b26SJan Kara if (inode->i_size > inode->i_sb->s_blocksize) {
112a1d47b26SJan Kara err = -ENAMETOOLONG;
113f33321b2SJan Kara goto out_unlock;
114a1d47b26SJan Kara }
115a1d47b26SJan Kara
11648d6d8ffSMarcin Slusarz if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
117382a2287SJan Kara symlink = iinfo->i_data + iinfo->i_lenEAttr;
11828de7948SCyrill Gorcunov } else {
11915a08f51SJan Kara bh = udf_bread(inode, 0, 0, &err);
120a1d47b26SJan Kara if (!bh) {
12115a08f51SJan Kara if (!err)
12215a08f51SJan Kara err = -EFSCORRUPTED;
123f33321b2SJan Kara goto out_err;
124a1d47b26SJan Kara }
1251da177e4SLinus Torvalds symlink = bh->b_data;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds
1280e5cc9a4SJan Kara err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
1293bf25cb4SJan Kara brelse(bh);
1300e5cc9a4SJan Kara if (err)
131f33321b2SJan Kara goto out_err;
1321da177e4SLinus Torvalds
1331da177e4SLinus Torvalds SetPageUptodate(page);
1341da177e4SLinus Torvalds unlock_page(page);
1351da177e4SLinus Torvalds return 0;
13628de7948SCyrill Gorcunov
137f33321b2SJan Kara out_err:
1381da177e4SLinus Torvalds SetPageError(page);
139f33321b2SJan Kara out_unlock:
1401da177e4SLinus Torvalds unlock_page(page);
1411da177e4SLinus Torvalds return err;
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds
udf_symlink_getattr(struct mnt_idmap * idmap,const struct path * path,struct kstat * stat,u32 request_mask,unsigned int flags)144b74d24f7SChristian Brauner static int udf_symlink_getattr(struct mnt_idmap *idmap,
145549c7297SChristian Brauner const struct path *path, struct kstat *stat,
146a528d35eSDavid Howells u32 request_mask, unsigned int flags)
147ad4d0532SJan Kara {
148a528d35eSDavid Howells struct dentry *dentry = path->dentry;
149ad4d0532SJan Kara struct inode *inode = d_backing_inode(dentry);
150ad4d0532SJan Kara struct page *page;
151ad4d0532SJan Kara
152*0d72b928SJeff Layton generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
153ad4d0532SJan Kara page = read_mapping_page(inode->i_mapping, 0, NULL);
154ad4d0532SJan Kara if (IS_ERR(page))
155ad4d0532SJan Kara return PTR_ERR(page);
156ad4d0532SJan Kara /*
157ad4d0532SJan Kara * UDF uses non-trivial encoding of symlinks so i_size does not match
158ad4d0532SJan Kara * number of characters reported by readlink(2) which apparently some
159ad4d0532SJan Kara * applications expect. Also POSIX says that "The value returned in the
160ad4d0532SJan Kara * st_size field shall be the length of the contents of the symbolic
161ad4d0532SJan Kara * link, and shall not count a trailing null if one is present." So
162ad4d0532SJan Kara * let's report the length of string returned by readlink(2) for
163ad4d0532SJan Kara * st_size.
164ad4d0532SJan Kara */
165ad4d0532SJan Kara stat->size = strlen(page_address(page));
166ad4d0532SJan Kara put_page(page);
167ad4d0532SJan Kara
168ad4d0532SJan Kara return 0;
169ad4d0532SJan Kara }
170ad4d0532SJan Kara
1711da177e4SLinus Torvalds /*
1721da177e4SLinus Torvalds * symlinks can't do much...
1731da177e4SLinus Torvalds */
174f5e54d6eSChristoph Hellwig const struct address_space_operations udf_symlink_aops = {
1750c698cc5SMatthew Wilcox (Oracle) .read_folio = udf_symlink_filler,
1761da177e4SLinus Torvalds };
177ad4d0532SJan Kara
178ad4d0532SJan Kara const struct inode_operations udf_symlink_inode_operations = {
179ad4d0532SJan Kara .get_link = page_get_link,
180ad4d0532SJan Kara .getattr = udf_symlink_getattr,
181ad4d0532SJan Kara };
182