2016-12-16 13:02:56 +03:00
/*
* Copyright ( C ) 2011 Novell Inc .
* Copyright ( C ) 2016 Red Hat , Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*/
# include <linux/fs.h>
# include <linux/mount.h>
# include <linux/slab.h>
2017-02-02 19:54:15 +03:00
# include <linux/cred.h>
2016-12-16 13:02:56 +03:00
# include <linux/xattr.h>
# include "overlayfs.h"
# include "ovl_entry.h"
int ovl_want_write ( struct dentry * dentry )
{
struct ovl_fs * ofs = dentry - > d_sb - > s_fs_info ;
return mnt_want_write ( ofs - > upper_mnt ) ;
}
void ovl_drop_write ( struct dentry * dentry )
{
struct ovl_fs * ofs = dentry - > d_sb - > s_fs_info ;
mnt_drop_write ( ofs - > upper_mnt ) ;
}
struct dentry * ovl_workdir ( struct dentry * dentry )
{
struct ovl_fs * ofs = dentry - > d_sb - > s_fs_info ;
return ofs - > workdir ;
}
const struct cred * ovl_override_creds ( struct super_block * sb )
{
struct ovl_fs * ofs = sb - > s_fs_info ;
return override_creds ( ofs - > creator_cred ) ;
}
2017-03-22 15:42:21 +03:00
struct super_block * ovl_same_sb ( struct super_block * sb )
{
struct ovl_fs * ofs = sb - > s_fs_info ;
return ofs - > same_sb ;
}
2016-12-16 13:02:56 +03:00
struct ovl_entry * ovl_alloc_entry ( unsigned int numlower )
{
size_t size = offsetof ( struct ovl_entry , lowerstack [ numlower ] ) ;
struct ovl_entry * oe = kzalloc ( size , GFP_KERNEL ) ;
if ( oe )
oe - > numlower = numlower ;
return oe ;
}
bool ovl_dentry_remote ( struct dentry * dentry )
{
return dentry - > d_flags &
( DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE |
DCACHE_OP_REAL ) ;
}
bool ovl_dentry_weird ( struct dentry * dentry )
{
return dentry - > d_flags & ( DCACHE_NEED_AUTOMOUNT |
DCACHE_MANAGE_TRANSIT |
DCACHE_OP_HASH |
DCACHE_OP_COMPARE ) ;
}
enum ovl_path_type ovl_path_type ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
enum ovl_path_type type = 0 ;
if ( oe - > __upperdentry ) {
type = __OVL_PATH_UPPER ;
/*
2017-04-23 23:12:34 +03:00
* Non - dir dentry can hold lower dentry of its copy up origin .
2016-12-16 13:02:56 +03:00
*/
2017-04-23 23:12:34 +03:00
if ( oe - > numlower ) {
type | = __OVL_PATH_ORIGIN ;
if ( d_is_dir ( dentry ) )
type | = __OVL_PATH_MERGE ;
}
2016-12-16 13:02:56 +03:00
} else {
if ( oe - > numlower > 1 )
type | = __OVL_PATH_MERGE ;
}
return type ;
}
void ovl_path_upper ( struct dentry * dentry , struct path * path )
{
struct ovl_fs * ofs = dentry - > d_sb - > s_fs_info ;
struct ovl_entry * oe = dentry - > d_fsdata ;
path - > mnt = ofs - > upper_mnt ;
path - > dentry = ovl_upperdentry_dereference ( oe ) ;
}
void ovl_path_lower ( struct dentry * dentry , struct path * path )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
2017-03-30 00:02:19 +03:00
* path = oe - > numlower ? oe - > lowerstack [ 0 ] : ( struct path ) { } ;
2016-12-16 13:02:56 +03:00
}
enum ovl_path_type ovl_path_real ( struct dentry * dentry , struct path * path )
{
enum ovl_path_type type = ovl_path_type ( dentry ) ;
if ( ! OVL_TYPE_UPPER ( type ) )
ovl_path_lower ( dentry , path ) ;
else
ovl_path_upper ( dentry , path ) ;
return type ;
}
struct dentry * ovl_dentry_upper ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
return ovl_upperdentry_dereference ( oe ) ;
}
static struct dentry * __ovl_dentry_lower ( struct ovl_entry * oe )
{
return oe - > numlower ? oe - > lowerstack [ 0 ] . dentry : NULL ;
}
struct dentry * ovl_dentry_lower ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
return __ovl_dentry_lower ( oe ) ;
}
struct dentry * ovl_dentry_real ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
struct dentry * realdentry ;
realdentry = ovl_upperdentry_dereference ( oe ) ;
if ( ! realdentry )
realdentry = __ovl_dentry_lower ( oe ) ;
return realdentry ;
}
struct ovl_dir_cache * ovl_dir_cache ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
return oe - > cache ;
}
void ovl_set_dir_cache ( struct dentry * dentry , struct ovl_dir_cache * cache )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
oe - > cache = cache ;
}
bool ovl_dentry_is_opaque ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
return oe - > opaque ;
}
2017-05-11 16:42:26 +03:00
bool ovl_dentry_is_impure ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
return oe - > impure ;
}
2016-12-16 13:02:56 +03:00
bool ovl_dentry_is_whiteout ( struct dentry * dentry )
{
return ! dentry - > d_inode & & ovl_dentry_is_opaque ( dentry ) ;
}
2016-12-16 13:02:57 +03:00
void ovl_dentry_set_opaque ( struct dentry * dentry )
2016-12-16 13:02:56 +03:00
{
struct ovl_entry * oe = dentry - > d_fsdata ;
2016-12-16 13:02:57 +03:00
oe - > opaque = true ;
2016-12-16 13:02:56 +03:00
}
2016-12-16 13:02:56 +03:00
bool ovl_redirect_dir ( struct super_block * sb )
{
struct ovl_fs * ofs = sb - > s_fs_info ;
2017-05-17 00:12:41 +03:00
return ofs - > config . redirect_dir & & ! ofs - > noxattr ;
2016-12-16 13:02:56 +03:00
}
const char * ovl_dentry_get_redirect ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
return oe - > redirect ;
}
void ovl_dentry_set_redirect ( struct dentry * dentry , const char * redirect )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
kfree ( oe - > redirect ) ;
oe - > redirect = redirect ;
}
2016-12-16 13:02:56 +03:00
void ovl_dentry_update ( struct dentry * dentry , struct dentry * upperdentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
WARN_ON ( ! inode_is_locked ( upperdentry - > d_parent - > d_inode ) ) ;
WARN_ON ( oe - > __upperdentry ) ;
/*
* Make sure upperdentry is consistent before making it visible to
* ovl_upperdentry_dereference ( ) .
*/
smp_wmb ( ) ;
oe - > __upperdentry = upperdentry ;
}
void ovl_inode_init ( struct inode * inode , struct inode * realinode , bool is_upper )
{
WRITE_ONCE ( inode - > i_private , ( unsigned long ) realinode |
( is_upper ? OVL_ISUPPER_MASK : 0 ) ) ;
}
void ovl_inode_update ( struct inode * inode , struct inode * upperinode )
{
WARN_ON ( ! upperinode ) ;
WARN_ON ( ! inode_unhashed ( inode ) ) ;
WRITE_ONCE ( inode - > i_private ,
( unsigned long ) upperinode | OVL_ISUPPER_MASK ) ;
if ( ! S_ISDIR ( upperinode - > i_mode ) )
__insert_inode_hash ( inode , ( unsigned long ) upperinode ) ;
}
void ovl_dentry_version_inc ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
WARN_ON ( ! inode_is_locked ( dentry - > d_inode ) ) ;
oe - > version + + ;
}
u64 ovl_dentry_version_get ( struct dentry * dentry )
{
struct ovl_entry * oe = dentry - > d_fsdata ;
WARN_ON ( ! inode_is_locked ( dentry - > d_inode ) ) ;
return oe - > version ;
}
bool ovl_is_whiteout ( struct dentry * dentry )
{
struct inode * inode = dentry - > d_inode ;
return inode & & IS_WHITEOUT ( inode ) ;
}
struct file * ovl_path_open ( struct path * path , int flags )
{
return dentry_open ( path , flags | O_NOATIME , current_cred ( ) ) ;
}
2017-01-17 07:34:56 +03:00
int ovl_copy_up_start ( struct dentry * dentry )
{
struct ovl_fs * ofs = dentry - > d_sb - > s_fs_info ;
struct ovl_entry * oe = dentry - > d_fsdata ;
int err ;
spin_lock ( & ofs - > copyup_wq . lock ) ;
err = wait_event_interruptible_locked ( ofs - > copyup_wq , ! oe - > copying ) ;
if ( ! err ) {
if ( oe - > __upperdentry )
err = 1 ; /* Already copied up */
else
oe - > copying = true ;
}
spin_unlock ( & ofs - > copyup_wq . lock ) ;
return err ;
}
void ovl_copy_up_end ( struct dentry * dentry )
{
struct ovl_fs * ofs = dentry - > d_sb - > s_fs_info ;
struct ovl_entry * oe = dentry - > d_fsdata ;
spin_lock ( & ofs - > copyup_wq . lock ) ;
oe - > copying = false ;
wake_up_locked ( & ofs - > copyup_wq ) ;
spin_unlock ( & ofs - > copyup_wq . lock ) ;
}
2017-05-17 00:12:40 +03:00
2017-05-24 15:29:33 +03:00
bool ovl_check_dir_xattr ( struct dentry * dentry , const char * name )
{
int res ;
char val ;
if ( ! d_is_dir ( dentry ) )
return false ;
res = vfs_getxattr ( dentry , name , & val , 1 ) ;
if ( res = = 1 & & val = = ' y ' )
return true ;
return false ;
}
2017-05-17 00:12:40 +03:00
int ovl_check_setxattr ( struct dentry * dentry , struct dentry * upperdentry ,
const char * name , const void * value , size_t size ,
int xerr )
{
int err ;
struct ovl_fs * ofs = dentry - > d_sb - > s_fs_info ;
if ( ofs - > noxattr )
return xerr ;
err = ovl_do_setxattr ( upperdentry , name , value , size , 0 ) ;
if ( err = = - EOPNOTSUPP ) {
pr_warn ( " overlayfs: cannot set %s xattr on upper \n " , name ) ;
ofs - > noxattr = true ;
return xerr ;
}
return err ;
}
2017-05-24 15:29:33 +03:00
int ovl_set_impure ( struct dentry * dentry , struct dentry * upperdentry )
{
int err ;
struct ovl_entry * oe = dentry - > d_fsdata ;
if ( oe - > impure )
return 0 ;
/*
* Do not fail when upper doesn ' t support xattrs .
* Upper inodes won ' t have origin nor redirect xattr anyway .
*/
err = ovl_check_setxattr ( dentry , upperdentry , OVL_XATTR_IMPURE ,
" y " , 1 , 0 ) ;
if ( ! err )
oe - > impure = true ;
return err ;
}