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>
# 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 ) ;
}
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 ;
/*
* Non - dir dentry can hold lower dentry from previous
* location .
*/
if ( oe - > numlower & & d_is_dir ( dentry ) )
type | = __OVL_PATH_MERGE ;
} 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 ;
* path = oe - > numlower ? oe - > lowerstack [ 0 ] : ( struct path ) { NULL , NULL } ;
}
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 ;
}
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 ;
return ofs - > config . redirect_dir ;
}
void ovl_clear_redirect_dir ( struct super_block * sb )
{
struct ovl_fs * ofs = sb - > s_fs_info ;
ofs - > config . redirect_dir = false ;
}
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 ( ) ) ;
}