2019-12-12 17:09:14 +03:00
// SPDX-License-Identifier: MIT
/*
* VirtualBox Guest Shared Folders support : Directory inode and file operations
*
* Copyright ( C ) 2006 - 2018 Oracle Corporation
*/
# include <linux/namei.h>
# include <linux/vbox_utils.h>
# include "vfsmod.h"
static int vboxsf_dir_open ( struct inode * inode , struct file * file )
{
struct vboxsf_sbi * sbi = VBOXSF_SBI ( inode - > i_sb ) ;
struct shfl_createparms params = { } ;
struct vboxsf_dir_info * sf_d ;
int err ;
sf_d = vboxsf_dir_info_alloc ( ) ;
if ( ! sf_d )
return - ENOMEM ;
params . handle = SHFL_HANDLE_NIL ;
params . create_flags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS |
SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ ;
err = vboxsf_create_at_dentry ( file_dentry ( file ) , & params ) ;
if ( err )
goto err_free_dir_info ;
if ( params . result ! = SHFL_FILE_EXISTS ) {
err = - ENOENT ;
goto err_close ;
}
err = vboxsf_dir_read_all ( sbi , sf_d , params . handle ) ;
if ( err )
goto err_close ;
vboxsf_close ( sbi - > root , params . handle ) ;
file - > private_data = sf_d ;
return 0 ;
err_close :
vboxsf_close ( sbi - > root , params . handle ) ;
err_free_dir_info :
vboxsf_dir_info_free ( sf_d ) ;
return err ;
}
static int vboxsf_dir_release ( struct inode * inode , struct file * file )
{
if ( file - > private_data )
vboxsf_dir_info_free ( file - > private_data ) ;
return 0 ;
}
static unsigned int vboxsf_get_d_type ( u32 mode )
{
unsigned int d_type ;
switch ( mode & SHFL_TYPE_MASK ) {
case SHFL_TYPE_FIFO :
d_type = DT_FIFO ;
break ;
case SHFL_TYPE_DEV_CHAR :
d_type = DT_CHR ;
break ;
case SHFL_TYPE_DIRECTORY :
d_type = DT_DIR ;
break ;
case SHFL_TYPE_DEV_BLOCK :
d_type = DT_BLK ;
break ;
case SHFL_TYPE_FILE :
d_type = DT_REG ;
break ;
case SHFL_TYPE_SYMLINK :
d_type = DT_LNK ;
break ;
case SHFL_TYPE_SOCKET :
d_type = DT_SOCK ;
break ;
case SHFL_TYPE_WHITEOUT :
d_type = DT_WHT ;
break ;
default :
d_type = DT_UNKNOWN ;
break ;
}
return d_type ;
}
static bool vboxsf_dir_emit ( struct file * dir , struct dir_context * ctx )
{
struct vboxsf_sbi * sbi = VBOXSF_SBI ( file_inode ( dir ) - > i_sb ) ;
struct vboxsf_dir_info * sf_d = dir - > private_data ;
struct shfl_dirinfo * info ;
struct vboxsf_dir_buf * b ;
unsigned int d_type ;
loff_t i , cur = 0 ;
ino_t fake_ino ;
void * end ;
int err ;
list_for_each_entry ( b , & sf_d - > info_list , head ) {
try_next_entry :
if ( ctx - > pos > = cur + b - > entries ) {
cur + = b - > entries ;
continue ;
}
/*
* Note the vboxsf_dir_info objects we are iterating over here
* are variable sized , so the info pointer may end up being
* unaligned . This is how we get the data from the host .
* Since vboxsf is only supported on x86 machines this is not
* a problem .
*/
for ( i = 0 , info = b - > buf ; i < ctx - > pos - cur ; i + + ) {
end = & info - > name . string . utf8 [ info - > name . size ] ;
/* Only happens if the host gives us corrupt data */
if ( WARN_ON ( end > ( b - > buf + b - > used ) ) )
return false ;
info = end ;
}
end = & info - > name . string . utf8 [ info - > name . size ] ;
if ( WARN_ON ( end > ( b - > buf + b - > used ) ) )
return false ;
/* Info now points to the right entry, emit it. */
d_type = vboxsf_get_d_type ( info - > info . attr . mode ) ;
/*
2020-03-14 07:13:11 +03:00
* On 32 - bit systems pos is 64 - bit signed , while ino is 32 - bit
2019-12-12 17:09:14 +03:00
* unsigned so fake_ino may overflow , check for this .
*/
if ( ( ino_t ) ( ctx - > pos + 1 ) ! = ( u64 ) ( ctx - > pos + 1 ) ) {
vbg_err ( " vboxsf: fake ino overflow, truncating dir \n " ) ;
return false ;
}
fake_ino = ctx - > pos + 1 ;
if ( sbi - > nls ) {
char d_name [ NAME_MAX ] ;
err = vboxsf_nlscpy ( sbi , d_name , NAME_MAX ,
info - > name . string . utf8 ,
info - > name . length ) ;
if ( err ) {
/* skip erroneous entry and proceed */
ctx - > pos + = 1 ;
goto try_next_entry ;
}
return dir_emit ( ctx , d_name , strlen ( d_name ) ,
fake_ino , d_type ) ;
}
return dir_emit ( ctx , info - > name . string . utf8 , info - > name . length ,
fake_ino , d_type ) ;
}
return false ;
}
static int vboxsf_dir_iterate ( struct file * dir , struct dir_context * ctx )
{
bool emitted ;
do {
emitted = vboxsf_dir_emit ( dir , ctx ) ;
if ( emitted )
ctx - > pos + = 1 ;
} while ( emitted ) ;
return 0 ;
}
const struct file_operations vboxsf_dir_fops = {
. open = vboxsf_dir_open ,
. iterate = vboxsf_dir_iterate ,
. release = vboxsf_dir_release ,
. read = generic_read_dir ,
. llseek = generic_file_llseek ,
} ;
/*
* This is called during name resolution / lookup to check if the @ dentry in
* the cache is still valid . the job is handled by vboxsf_inode_revalidate .
*/
static int vboxsf_dentry_revalidate ( struct dentry * dentry , unsigned int flags )
{
if ( flags & LOOKUP_RCU )
return - ECHILD ;
if ( d_really_is_positive ( dentry ) )
return vboxsf_inode_revalidate ( dentry ) = = 0 ;
else
return vboxsf_stat_dentry ( dentry , NULL ) = = - ENOENT ;
}
const struct dentry_operations vboxsf_dentry_ops = {
. d_revalidate = vboxsf_dentry_revalidate
} ;
/* iops */
static struct dentry * vboxsf_dir_lookup ( struct inode * parent ,
struct dentry * dentry ,
unsigned int flags )
{
struct vboxsf_sbi * sbi = VBOXSF_SBI ( parent - > i_sb ) ;
struct shfl_fsobjinfo fsinfo ;
struct inode * inode ;
int err ;
dentry - > d_time = jiffies ;
err = vboxsf_stat_dentry ( dentry , & fsinfo ) ;
if ( err ) {
inode = ( err = = - ENOENT ) ? NULL : ERR_PTR ( err ) ;
} else {
inode = vboxsf_new_inode ( parent - > i_sb ) ;
if ( ! IS_ERR ( inode ) )
2021-02-14 08:12:23 +03:00
vboxsf_init_inode ( sbi , inode , & fsinfo , false ) ;
2019-12-12 17:09:14 +03:00
}
return d_splice_alias ( inode , dentry ) ;
}
static int vboxsf_dir_instantiate ( struct inode * parent , struct dentry * dentry ,
struct shfl_fsobjinfo * info )
{
struct vboxsf_sbi * sbi = VBOXSF_SBI ( parent - > i_sb ) ;
struct vboxsf_inode * sf_i ;
struct inode * inode ;
inode = vboxsf_new_inode ( parent - > i_sb ) ;
if ( IS_ERR ( inode ) )
return PTR_ERR ( inode ) ;
sf_i = VBOXSF_I ( inode ) ;
/* The host may have given us different attr then requested */
sf_i - > force_restat = 1 ;
2021-02-14 08:12:23 +03:00
vboxsf_init_inode ( sbi , inode , info , false ) ;
2019-12-12 17:09:14 +03:00
d_instantiate ( dentry , inode ) ;
return 0 ;
}
static int vboxsf_dir_create ( struct inode * parent , struct dentry * dentry ,
2021-01-21 12:22:27 +03:00
umode_t mode , bool is_dir , bool excl , u64 * handle_ret )
2019-12-12 17:09:14 +03:00
{
struct vboxsf_inode * sf_parent_i = VBOXSF_I ( parent ) ;
struct vboxsf_sbi * sbi = VBOXSF_SBI ( parent - > i_sb ) ;
struct shfl_createparms params = { } ;
int err ;
params . handle = SHFL_HANDLE_NIL ;
2021-01-21 12:08:59 +03:00
params . create_flags = SHFL_CF_ACT_CREATE_IF_NEW | SHFL_CF_ACCESS_READWRITE ;
if ( is_dir )
params . create_flags | = SHFL_CF_DIRECTORY ;
if ( excl )
params . create_flags | = SHFL_CF_ACT_FAIL_IF_EXISTS ;
2019-12-12 17:09:14 +03:00
params . info . attr . mode = ( mode & 0777 ) |
( is_dir ? SHFL_TYPE_DIRECTORY : SHFL_TYPE_FILE ) ;
params . info . attr . additional = SHFLFSOBJATTRADD_NOTHING ;
err = vboxsf_create_at_dentry ( dentry , & params ) ;
if ( err )
return err ;
if ( params . result ! = SHFL_FILE_CREATED )
return - EPERM ;
err = vboxsf_dir_instantiate ( parent , dentry , & params . info ) ;
if ( err )
2021-01-21 12:22:27 +03:00
goto out ;
2019-12-12 17:09:14 +03:00
/* parent directory access/change time changed */
sf_parent_i - > force_restat = 1 ;
2021-01-21 12:22:27 +03:00
out :
if ( err = = 0 & & handle_ret )
* handle_ret = params . handle ;
else
vboxsf_close ( sbi - > root , params . handle ) ;
return err ;
2019-12-12 17:09:14 +03:00
}
2023-01-13 14:49:13 +03:00
static int vboxsf_dir_mkfile ( struct mnt_idmap * idmap ,
2021-01-21 16:19:43 +03:00
struct inode * parent , struct dentry * dentry ,
2019-12-12 17:09:14 +03:00
umode_t mode , bool excl )
{
2021-01-21 12:22:27 +03:00
return vboxsf_dir_create ( parent , dentry , mode , false , excl , NULL ) ;
2019-12-12 17:09:14 +03:00
}
2023-01-13 14:49:15 +03:00
static int vboxsf_dir_mkdir ( struct mnt_idmap * idmap ,
2021-01-21 16:19:43 +03:00
struct inode * parent , struct dentry * dentry ,
2019-12-12 17:09:14 +03:00
umode_t mode )
{
2021-01-21 12:22:27 +03:00
return vboxsf_dir_create ( parent , dentry , mode , true , true , NULL ) ;
2019-12-12 17:09:14 +03:00
}
2021-01-21 14:54:18 +03:00
static int vboxsf_dir_atomic_open ( struct inode * parent , struct dentry * dentry ,
struct file * file , unsigned int flags , umode_t mode )
{
struct vboxsf_sbi * sbi = VBOXSF_SBI ( parent - > i_sb ) ;
struct vboxsf_handle * sf_handle ;
struct dentry * res = NULL ;
u64 handle ;
int err ;
if ( d_in_lookup ( dentry ) ) {
res = vboxsf_dir_lookup ( parent , dentry , 0 ) ;
if ( IS_ERR ( res ) )
return PTR_ERR ( res ) ;
if ( res )
dentry = res ;
}
/* Only creates */
if ( ! ( flags & O_CREAT ) | | d_really_is_positive ( dentry ) )
return finish_no_open ( file , res ) ;
err = vboxsf_dir_create ( parent , dentry , mode , false , flags & O_EXCL , & handle ) ;
if ( err )
goto out ;
sf_handle = vboxsf_create_sf_handle ( d_inode ( dentry ) , handle , SHFL_CF_ACCESS_READWRITE ) ;
if ( IS_ERR ( sf_handle ) ) {
vboxsf_close ( sbi - > root , handle ) ;
err = PTR_ERR ( sf_handle ) ;
goto out ;
}
err = finish_open ( file , dentry , generic_file_open ) ;
if ( err ) {
/* This also closes the handle passed to vboxsf_create_sf_handle() */
vboxsf_release_sf_handle ( d_inode ( dentry ) , sf_handle ) ;
goto out ;
}
file - > private_data = sf_handle ;
file - > f_mode | = FMODE_CREATED ;
out :
dput ( res ) ;
return err ;
}
2019-12-12 17:09:14 +03:00
static int vboxsf_dir_unlink ( struct inode * parent , struct dentry * dentry )
{
struct vboxsf_sbi * sbi = VBOXSF_SBI ( parent - > i_sb ) ;
struct vboxsf_inode * sf_parent_i = VBOXSF_I ( parent ) ;
struct inode * inode = d_inode ( dentry ) ;
struct shfl_string * path ;
u32 flags ;
int err ;
if ( S_ISDIR ( inode - > i_mode ) )
flags = SHFL_REMOVE_DIR ;
else
flags = SHFL_REMOVE_FILE ;
if ( S_ISLNK ( inode - > i_mode ) )
flags | = SHFL_REMOVE_SYMLINK ;
path = vboxsf_path_from_dentry ( sbi , dentry ) ;
if ( IS_ERR ( path ) )
return PTR_ERR ( path ) ;
err = vboxsf_remove ( sbi - > root , path , flags ) ;
__putname ( path ) ;
if ( err )
return err ;
/* parent directory access/change time changed */
sf_parent_i - > force_restat = 1 ;
return 0 ;
}
2023-01-13 14:49:17 +03:00
static int vboxsf_dir_rename ( struct mnt_idmap * idmap ,
2021-01-21 16:19:43 +03:00
struct inode * old_parent ,
2019-12-12 17:09:14 +03:00
struct dentry * old_dentry ,
struct inode * new_parent ,
struct dentry * new_dentry ,
unsigned int flags )
{
struct vboxsf_sbi * sbi = VBOXSF_SBI ( old_parent - > i_sb ) ;
struct vboxsf_inode * sf_old_parent_i = VBOXSF_I ( old_parent ) ;
struct vboxsf_inode * sf_new_parent_i = VBOXSF_I ( new_parent ) ;
u32 shfl_flags = SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS ;
struct shfl_string * old_path , * new_path ;
int err ;
if ( flags )
return - EINVAL ;
old_path = vboxsf_path_from_dentry ( sbi , old_dentry ) ;
if ( IS_ERR ( old_path ) )
return PTR_ERR ( old_path ) ;
new_path = vboxsf_path_from_dentry ( sbi , new_dentry ) ;
if ( IS_ERR ( new_path ) ) {
err = PTR_ERR ( new_path ) ;
goto err_put_old_path ;
}
if ( d_inode ( old_dentry ) - > i_mode & S_IFDIR )
shfl_flags = 0 ;
err = vboxsf_rename ( sbi - > root , old_path , new_path , shfl_flags ) ;
if ( err = = 0 ) {
/* parent directories access/change time changed */
sf_new_parent_i - > force_restat = 1 ;
sf_old_parent_i - > force_restat = 1 ;
}
__putname ( new_path ) ;
err_put_old_path :
__putname ( old_path ) ;
return err ;
}
2023-01-13 14:49:14 +03:00
static int vboxsf_dir_symlink ( struct mnt_idmap * idmap ,
2021-01-21 16:19:43 +03:00
struct inode * parent , struct dentry * dentry ,
2019-12-12 17:09:14 +03:00
const char * symname )
{
struct vboxsf_inode * sf_parent_i = VBOXSF_I ( parent ) ;
struct vboxsf_sbi * sbi = VBOXSF_SBI ( parent - > i_sb ) ;
int symname_size = strlen ( symname ) + 1 ;
struct shfl_string * path , * ssymname ;
struct shfl_fsobjinfo info ;
int err ;
path = vboxsf_path_from_dentry ( sbi , dentry ) ;
if ( IS_ERR ( path ) )
return PTR_ERR ( path ) ;
ssymname = kmalloc ( SHFLSTRING_HEADER_SIZE + symname_size , GFP_KERNEL ) ;
if ( ! ssymname ) {
__putname ( path ) ;
return - ENOMEM ;
}
ssymname - > length = symname_size - 1 ;
ssymname - > size = symname_size ;
memcpy ( ssymname - > string . utf8 , symname , symname_size ) ;
err = vboxsf_symlink ( sbi - > root , path , ssymname , & info ) ;
kfree ( ssymname ) ;
__putname ( path ) ;
if ( err ) {
/* -EROFS means symlinks are note support -> -EPERM */
return ( err = = - EROFS ) ? - EPERM : err ;
}
err = vboxsf_dir_instantiate ( parent , dentry , & info ) ;
if ( err )
return err ;
/* parent directory access/change time changed */
sf_parent_i - > force_restat = 1 ;
return 0 ;
}
const struct inode_operations vboxsf_dir_iops = {
. lookup = vboxsf_dir_lookup ,
. create = vboxsf_dir_mkfile ,
. mkdir = vboxsf_dir_mkdir ,
2021-01-21 14:54:18 +03:00
. atomic_open = vboxsf_dir_atomic_open ,
2019-12-12 17:09:14 +03:00
. rmdir = vboxsf_dir_unlink ,
. unlink = vboxsf_dir_unlink ,
. rename = vboxsf_dir_rename ,
. symlink = vboxsf_dir_symlink ,
. getattr = vboxsf_getattr ,
. setattr = vboxsf_setattr ,
} ;