2005-04-16 15:20:36 -07:00
/*
* symlink . c
*
* PURPOSE
* Symlink handling routines for the OSTA - UDF ( tm ) filesystem .
*
* COPYRIGHT
* This file is distributed under the terms of the GNU General Public
* License ( GPL ) . Copies of the GPL can be obtained from :
* ftp : //prep.ai.mit.edu/pub/gnu/GPL
* Each contributing author retains all rights to their own work .
*
* ( C ) 1998 - 2001 Ben Fennema
2007-07-21 04:37:18 -07:00
* ( C ) 1999 Stelias Computing Inc
2005-04-16 15:20:36 -07:00
*
* HISTORY
*
* 04 / 16 / 99 blf Created .
*
*/
# include "udfdecl.h"
2014-06-18 19:38:24 +02:00
# include <linux/uaccess.h>
2005-04-16 15:20:36 -07:00
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/time.h>
# include <linux/mm.h>
# include <linux/stat.h>
# include <linux/pagemap.h>
# include "udf_i.h"
2014-12-18 22:37:50 +01:00
static int udf_pc_to_char ( struct super_block * sb , unsigned char * from ,
int fromlen , unsigned char * to , int tolen )
2005-04-16 15:20:36 -07:00
{
struct pathComponent * pc ;
int elen = 0 ;
2014-12-18 22:37:50 +01:00
int comp_len ;
2010-01-31 21:28:48 -05:00
unsigned char * p = to ;
2005-04-16 15:20:36 -07:00
2014-12-18 22:37:50 +01:00
/* Reserve one byte for terminating \0 */
tolen - - ;
2007-07-19 01:47:43 -07:00
while ( elen < fromlen ) {
2005-04-16 15:20:36 -07:00
pc = ( struct pathComponent * ) ( from + elen ) ;
2014-12-19 14:27:55 +01:00
elen + = sizeof ( struct pathComponent ) ;
2007-07-19 01:47:43 -07:00
switch ( pc - > componentType ) {
case 1 :
2011-12-12 15:13:50 +01:00
/*
* Symlink points to some place which should be agreed
* upon between originator and receiver of the media . Ignore .
*/
2014-12-19 14:27:55 +01:00
if ( pc - > lengthComponentIdent > 0 ) {
elen + = pc - > lengthComponentIdent ;
2011-12-12 15:13:50 +01:00
break ;
2014-12-19 14:27:55 +01:00
}
2011-12-12 15:13:50 +01:00
/* Fall through */
case 2 :
2014-12-18 22:37:50 +01:00
if ( tolen = = 0 )
return - ENAMETOOLONG ;
2011-12-12 15:13:50 +01:00
p = to ;
* p + + = ' / ' ;
2014-12-18 22:37:50 +01:00
tolen - - ;
2007-07-19 01:47:43 -07:00
break ;
case 3 :
2014-12-18 22:37:50 +01:00
if ( tolen < 3 )
return - ENAMETOOLONG ;
2007-07-19 01:47:43 -07:00
memcpy ( p , " ../ " , 3 ) ;
p + = 3 ;
2014-12-18 22:37:50 +01:00
tolen - = 3 ;
2007-07-19 01:47:43 -07:00
break ;
case 4 :
2014-12-18 22:37:50 +01:00
if ( tolen < 2 )
return - ENAMETOOLONG ;
2007-07-19 01:47:43 -07:00
memcpy ( p , " ./ " , 2 ) ;
p + = 2 ;
2014-12-18 22:37:50 +01:00
tolen - = 2 ;
2007-07-19 01:47:43 -07:00
/* that would be . - just ignore */
break ;
case 5 :
2014-12-19 14:27:55 +01:00
elen + = pc - > lengthComponentIdent ;
if ( elen > fromlen )
return - EIO ;
2014-12-18 22:37:50 +01:00
comp_len = udf_get_filename ( sb , pc - > componentIdent ,
pc - > lengthComponentIdent ,
p , tolen ) ;
2015-04-08 21:23:51 +02:00
if ( comp_len < 0 )
return comp_len ;
2014-12-18 22:37:50 +01:00
p + = comp_len ;
tolen - = comp_len ;
if ( tolen = = 0 )
return - ENAMETOOLONG ;
2007-07-19 01:47:43 -07:00
* p + + = ' / ' ;
2014-12-18 22:37:50 +01:00
tolen - - ;
2007-07-19 01:47:43 -07:00
break ;
2005-04-16 15:20:36 -07:00
}
}
2007-07-19 01:47:43 -07:00
if ( p > to + 1 )
2005-04-16 15:20:36 -07:00
p [ - 1 ] = ' \0 ' ;
else
p [ 0 ] = ' \0 ' ;
2014-12-18 22:37:50 +01:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static int udf_symlink_filler ( struct file * file , struct page * page )
{
struct inode * inode = page - > mapping - > host ;
struct buffer_head * bh = NULL ;
2010-01-31 21:28:48 -05:00
unsigned char * symlink ;
2014-12-19 12:21:47 +01:00
int err ;
2015-11-17 01:07:57 -05:00
unsigned char * p = page_address ( page ) ;
2008-02-08 04:20:44 -08:00
struct udf_inode_info * iinfo ;
2010-11-16 18:40:47 +01:00
uint32_t pos ;
2005-04-16 15:20:36 -07:00
2014-12-19 12:21:47 +01:00
/* We don't support symlinks longer than one block */
if ( inode - > i_size > inode - > i_sb - > s_blocksize ) {
err = - ENAMETOOLONG ;
goto out_unmap ;
}
2008-02-08 04:20:44 -08:00
iinfo = UDF_I ( inode ) ;
2010-11-16 18:40:47 +01:00
pos = udf_block_map ( inode , 0 ) ;
down_read ( & iinfo - > i_data_sem ) ;
2008-02-08 04:20:44 -08:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_IN_ICB ) {
symlink = iinfo - > i_ext . i_data + iinfo - > i_lenEAttr ;
2007-07-21 04:37:18 -07:00
} else {
2010-11-16 18:40:47 +01:00
bh = sb_bread ( inode - > i_sb , pos ) ;
2005-04-16 15:20:36 -07:00
2014-12-19 12:21:47 +01:00
if ( ! bh ) {
err = - EIO ;
goto out_unlock_inode ;
}
2005-04-16 15:20:36 -07:00
symlink = bh - > b_data ;
}
2014-12-18 22:37:50 +01:00
err = udf_pc_to_char ( inode - > i_sb , symlink , inode - > i_size , p , PAGE_SIZE ) ;
2007-05-08 00:35:16 -07:00
brelse ( bh ) ;
2014-12-18 22:37:50 +01:00
if ( err )
goto out_unlock_inode ;
2005-04-16 15:20:36 -07:00
2010-11-16 18:40:47 +01:00
up_read ( & iinfo - > i_data_sem ) ;
2005-04-16 15:20:36 -07:00
SetPageUptodate ( page ) ;
unlock_page ( page ) ;
return 0 ;
2007-07-21 04:37:18 -07:00
2014-12-19 12:21:47 +01:00
out_unlock_inode :
2010-11-16 18:40:47 +01:00
up_read ( & iinfo - > i_data_sem ) ;
2005-04-16 15:20:36 -07:00
SetPageError ( page ) ;
2014-12-19 12:21:47 +01:00
out_unmap :
2005-04-16 15:20:36 -07:00
unlock_page ( page ) ;
return err ;
}
/*
* symlinks can ' t do much . . .
*/
2006-06-28 04:26:44 -07:00
const struct address_space_operations udf_symlink_aops = {
2007-07-21 04:37:18 -07:00
. readpage = udf_symlink_filler ,
2005-04-16 15:20:36 -07:00
} ;