cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

symlink.c (4436B)


      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
     32static 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			fallthrough;
     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
    104static int udf_symlink_filler(struct file *file, struct folio *folio)
    105{
    106	struct page *page = &folio->page;
    107	struct inode *inode = page->mapping->host;
    108	struct buffer_head *bh = NULL;
    109	unsigned char *symlink;
    110	int err;
    111	unsigned char *p = page_address(page);
    112	struct udf_inode_info *iinfo;
    113	uint32_t pos;
    114
    115	/* We don't support symlinks longer than one block */
    116	if (inode->i_size > inode->i_sb->s_blocksize) {
    117		err = -ENAMETOOLONG;
    118		goto out_unmap;
    119	}
    120
    121	iinfo = UDF_I(inode);
    122	pos = udf_block_map(inode, 0);
    123
    124	down_read(&iinfo->i_data_sem);
    125	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
    126		symlink = iinfo->i_data + iinfo->i_lenEAttr;
    127	} else {
    128		bh = sb_bread(inode->i_sb, pos);
    129
    130		if (!bh) {
    131			err = -EIO;
    132			goto out_unlock_inode;
    133		}
    134
    135		symlink = bh->b_data;
    136	}
    137
    138	err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
    139	brelse(bh);
    140	if (err)
    141		goto out_unlock_inode;
    142
    143	up_read(&iinfo->i_data_sem);
    144	SetPageUptodate(page);
    145	unlock_page(page);
    146	return 0;
    147
    148out_unlock_inode:
    149	up_read(&iinfo->i_data_sem);
    150	SetPageError(page);
    151out_unmap:
    152	unlock_page(page);
    153	return err;
    154}
    155
    156static int udf_symlink_getattr(struct user_namespace *mnt_userns,
    157			       const struct path *path, struct kstat *stat,
    158			       u32 request_mask, unsigned int flags)
    159{
    160	struct dentry *dentry = path->dentry;
    161	struct inode *inode = d_backing_inode(dentry);
    162	struct page *page;
    163
    164	generic_fillattr(&init_user_ns, inode, stat);
    165	page = read_mapping_page(inode->i_mapping, 0, NULL);
    166	if (IS_ERR(page))
    167		return PTR_ERR(page);
    168	/*
    169	 * UDF uses non-trivial encoding of symlinks so i_size does not match
    170	 * number of characters reported by readlink(2) which apparently some
    171	 * applications expect. Also POSIX says that "The value returned in the
    172	 * st_size field shall be the length of the contents of the symbolic
    173	 * link, and shall not count a trailing null if one is present." So
    174	 * let's report the length of string returned by readlink(2) for
    175	 * st_size.
    176	 */
    177	stat->size = strlen(page_address(page));
    178	put_page(page);
    179
    180	return 0;
    181}
    182
    183/*
    184 * symlinks can't do much...
    185 */
    186const struct address_space_operations udf_symlink_aops = {
    187	.read_folio		= udf_symlink_filler,
    188};
    189
    190const struct inode_operations udf_symlink_inode_operations = {
    191	.get_link	= page_get_link,
    192	.getattr	= udf_symlink_getattr,
    193};