Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * symlink.c
0003  *
0004  * PURPOSE
0005  *  Symlink handling routines for the OSTA-UDF(tm) filesystem.
0006  *
0007  * COPYRIGHT
0008  *  This file is distributed under the terms of the GNU General Public
0009  *  License (GPL). Copies of the GPL can be obtained from:
0010  *      ftp://prep.ai.mit.edu/pub/gnu/GPL
0011  *  Each contributing author retains all rights to their own work.
0012  *
0013  *  (C) 1998-2001 Ben Fennema
0014  *  (C) 1999 Stelias Computing Inc
0015  *
0016  * HISTORY
0017  *
0018  *  04/16/99 blf  Created.
0019  *
0020  */
0021 
0022 #include "udfdecl.h"
0023 #include <linux/uaccess.h>
0024 #include <linux/errno.h>
0025 #include <linux/fs.h>
0026 #include <linux/time.h>
0027 #include <linux/mm.h>
0028 #include <linux/stat.h>
0029 #include <linux/pagemap.h>
0030 #include "udf_i.h"
0031 
0032 static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
0033               int fromlen, unsigned char *to, int tolen)
0034 {
0035     struct pathComponent *pc;
0036     int elen = 0;
0037     int comp_len;
0038     unsigned char *p = to;
0039 
0040     /* Reserve one byte for terminating \0 */
0041     tolen--;
0042     while (elen < fromlen) {
0043         pc = (struct pathComponent *)(from + elen);
0044         elen += sizeof(struct pathComponent);
0045         switch (pc->componentType) {
0046         case 1:
0047             /*
0048              * Symlink points to some place which should be agreed
0049              * upon between originator and receiver of the media. Ignore.
0050              */
0051             if (pc->lengthComponentIdent > 0) {
0052                 elen += pc->lengthComponentIdent;
0053                 break;
0054             }
0055             fallthrough;
0056         case 2:
0057             if (tolen == 0)
0058                 return -ENAMETOOLONG;
0059             p = to;
0060             *p++ = '/';
0061             tolen--;
0062             break;
0063         case 3:
0064             if (tolen < 3)
0065                 return -ENAMETOOLONG;
0066             memcpy(p, "../", 3);
0067             p += 3;
0068             tolen -= 3;
0069             break;
0070         case 4:
0071             if (tolen < 2)
0072                 return -ENAMETOOLONG;
0073             memcpy(p, "./", 2);
0074             p += 2;
0075             tolen -= 2;
0076             /* that would be . - just ignore */
0077             break;
0078         case 5:
0079             elen += pc->lengthComponentIdent;
0080             if (elen > fromlen)
0081                 return -EIO;
0082             comp_len = udf_get_filename(sb, pc->componentIdent,
0083                             pc->lengthComponentIdent,
0084                             p, tolen);
0085             if (comp_len < 0)
0086                 return comp_len;
0087 
0088             p += comp_len;
0089             tolen -= comp_len;
0090             if (tolen == 0)
0091                 return -ENAMETOOLONG;
0092             *p++ = '/';
0093             tolen--;
0094             break;
0095         }
0096     }
0097     if (p > to + 1)
0098         p[-1] = '\0';
0099     else
0100         p[0] = '\0';
0101     return 0;
0102 }
0103 
0104 static int udf_symlink_filler(struct file *file, struct folio *folio)
0105 {
0106     struct page *page = &folio->page;
0107     struct inode *inode = page->mapping->host;
0108     struct buffer_head *bh = NULL;
0109     unsigned char *symlink;
0110     int err;
0111     unsigned char *p = page_address(page);
0112     struct udf_inode_info *iinfo;
0113     uint32_t pos;
0114 
0115     /* We don't support symlinks longer than one block */
0116     if (inode->i_size > inode->i_sb->s_blocksize) {
0117         err = -ENAMETOOLONG;
0118         goto out_unmap;
0119     }
0120 
0121     iinfo = UDF_I(inode);
0122     pos = udf_block_map(inode, 0);
0123 
0124     down_read(&iinfo->i_data_sem);
0125     if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
0126         symlink = iinfo->i_data + iinfo->i_lenEAttr;
0127     } else {
0128         bh = sb_bread(inode->i_sb, pos);
0129 
0130         if (!bh) {
0131             err = -EIO;
0132             goto out_unlock_inode;
0133         }
0134 
0135         symlink = bh->b_data;
0136     }
0137 
0138     err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
0139     brelse(bh);
0140     if (err)
0141         goto out_unlock_inode;
0142 
0143     up_read(&iinfo->i_data_sem);
0144     SetPageUptodate(page);
0145     unlock_page(page);
0146     return 0;
0147 
0148 out_unlock_inode:
0149     up_read(&iinfo->i_data_sem);
0150     SetPageError(page);
0151 out_unmap:
0152     unlock_page(page);
0153     return err;
0154 }
0155 
0156 static int udf_symlink_getattr(struct user_namespace *mnt_userns,
0157                    const struct path *path, struct kstat *stat,
0158                    u32 request_mask, unsigned int flags)
0159 {
0160     struct dentry *dentry = path->dentry;
0161     struct inode *inode = d_backing_inode(dentry);
0162     struct page *page;
0163 
0164     generic_fillattr(&init_user_ns, inode, stat);
0165     page = read_mapping_page(inode->i_mapping, 0, NULL);
0166     if (IS_ERR(page))
0167         return PTR_ERR(page);
0168     /*
0169      * UDF uses non-trivial encoding of symlinks so i_size does not match
0170      * number of characters reported by readlink(2) which apparently some
0171      * applications expect. Also POSIX says that "The value returned in the
0172      * st_size field shall be the length of the contents of the symbolic
0173      * link, and shall not count a trailing null if one is present." So
0174      * let's report the length of string returned by readlink(2) for
0175      * st_size.
0176      */
0177     stat->size = strlen(page_address(page));
0178     put_page(page);
0179 
0180     return 0;
0181 }
0182 
0183 /*
0184  * symlinks can't do much...
0185  */
0186 const struct address_space_operations udf_symlink_aops = {
0187     .read_folio     = udf_symlink_filler,
0188 };
0189 
0190 const struct inode_operations udf_symlink_inode_operations = {
0191     .get_link   = page_get_link,
0192     .getattr    = udf_symlink_getattr,
0193 };