2017-03-17 09:18:50 +03:00
// SPDX-License-Identifier: GPL-2.0
# include "bcachefs.h"
# include "bkey_methods.h"
# include "btree_update.h"
# include "extents.h"
# include "dirent.h"
# include "fs.h"
# include "keylist.h"
# include "str_hash.h"
2021-03-16 07:28:17 +03:00
# include "subvolume.h"
2017-03-17 09:18:50 +03:00
# include <linux/dcache.h>
unsigned bch2_dirent_name_bytes ( struct bkey_s_c_dirent d )
{
unsigned len = bkey_val_bytes ( d . k ) -
offsetof ( struct bch_dirent , d_name ) ;
2018-08-22 02:42:00 +03:00
return strnlen ( d . v - > d_name , len ) ;
2017-03-17 09:18:50 +03:00
}
static u64 bch2_dirent_hash ( const struct bch_hash_info * info ,
const struct qstr * name )
{
struct bch_str_hash_ctx ctx ;
bch2_str_hash_init ( & ctx , info ) ;
bch2_str_hash_update ( & ctx , info , name - > name , name - > len ) ;
/* [0,2) reserved for dots */
return max_t ( u64 , bch2_str_hash_end ( & ctx , info ) , 2 ) ;
}
static u64 dirent_hash_key ( const struct bch_hash_info * info , const void * key )
{
return bch2_dirent_hash ( info , key ) ;
}
static u64 dirent_hash_bkey ( const struct bch_hash_info * info , struct bkey_s_c k )
{
struct bkey_s_c_dirent d = bkey_s_c_to_dirent ( k ) ;
struct qstr name = QSTR_INIT ( d . v - > d_name , bch2_dirent_name_bytes ( d ) ) ;
return bch2_dirent_hash ( info , & name ) ;
}
static bool dirent_cmp_key ( struct bkey_s_c _l , const void * _r )
{
struct bkey_s_c_dirent l = bkey_s_c_to_dirent ( _l ) ;
int len = bch2_dirent_name_bytes ( l ) ;
const struct qstr * r = _r ;
return len - r - > len ? : memcmp ( l . v - > d_name , r - > name , len ) ;
}
static bool dirent_cmp_bkey ( struct bkey_s_c _l , struct bkey_s_c _r )
{
struct bkey_s_c_dirent l = bkey_s_c_to_dirent ( _l ) ;
struct bkey_s_c_dirent r = bkey_s_c_to_dirent ( _r ) ;
int l_len = bch2_dirent_name_bytes ( l ) ;
int r_len = bch2_dirent_name_bytes ( r ) ;
return l_len - r_len ? : memcmp ( l . v - > d_name , r . v - > d_name , l_len ) ;
}
2021-10-12 19:06:02 +03:00
static bool dirent_is_visible ( subvol_inum inum , struct bkey_s_c k )
{
struct bkey_s_c_dirent d = bkey_s_c_to_dirent ( k ) ;
if ( d . v - > d_type = = DT_SUBVOL )
return le32_to_cpu ( d . v - > d_parent_subvol ) = = inum . subvol ;
return true ;
}
2017-03-17 09:18:50 +03:00
const struct bch_hash_desc bch2_dirent_hash_desc = {
2021-02-21 03:27:37 +03:00
. btree_id = BTREE_ID_dirents ,
2018-11-01 22:10:01 +03:00
. key_type = KEY_TYPE_dirent ,
2017-03-17 09:18:50 +03:00
. hash_key = dirent_hash_key ,
. hash_bkey = dirent_hash_bkey ,
. cmp_key = dirent_cmp_key ,
. cmp_bkey = dirent_cmp_bkey ,
2021-10-12 19:06:02 +03:00
. is_visible = dirent_is_visible ,
2017-03-17 09:18:50 +03:00
} ;
const char * bch2_dirent_invalid ( const struct bch_fs * c , struct bkey_s_c k )
{
2018-11-01 22:10:01 +03:00
struct bkey_s_c_dirent d = bkey_s_c_to_dirent ( k ) ;
2017-03-17 09:18:50 +03:00
unsigned len ;
2018-11-01 22:10:01 +03:00
if ( bkey_val_bytes ( k . k ) < sizeof ( struct bch_dirent ) )
return " value too small " ;
2017-03-17 09:18:50 +03:00
2018-11-01 22:10:01 +03:00
len = bch2_dirent_name_bytes ( d ) ;
if ( ! len )
return " empty name " ;
2017-03-17 09:18:50 +03:00
2021-04-07 03:11:28 +03:00
if ( bkey_val_u64s ( k . k ) > dirent_val_u64s ( len ) )
2018-11-01 22:10:01 +03:00
return " value too big " ;
2017-03-17 09:18:50 +03:00
2018-11-01 22:10:01 +03:00
if ( len > BCH_NAME_MAX )
return " dirent name too big " ;
2017-03-17 09:18:50 +03:00
2021-04-07 03:11:28 +03:00
if ( len = = 1 & & ! memcmp ( d . v - > d_name , " . " , 1 ) )
return " invalid name " ;
if ( len = = 2 & & ! memcmp ( d . v - > d_name , " .. " , 2 ) )
return " invalid name " ;
if ( memchr ( d . v - > d_name , ' / ' , len ) )
return " invalid name " ;
2021-03-16 07:42:25 +03:00
if ( d . v - > d_type ! = DT_SUBVOL & &
le64_to_cpu ( d . v - > d_inum ) = = d . k - > p . inode )
2021-04-07 03:11:28 +03:00
return " dirent points to own directory " ;
2018-11-01 22:10:01 +03:00
return NULL ;
2017-03-17 09:18:50 +03:00
}
2018-11-09 09:24:07 +03:00
void bch2_dirent_to_text ( struct printbuf * out , struct bch_fs * c ,
struct bkey_s_c k )
2017-03-17 09:18:50 +03:00
{
2018-11-01 22:10:01 +03:00
struct bkey_s_c_dirent d = bkey_s_c_to_dirent ( k ) ;
2022-02-20 12:52:44 +03:00
pr_buf ( out , " %.*s -> %llu type %s " ,
bch2_dirent_name_bytes ( d ) ,
d . v - > d_name ,
2021-10-12 19:06:02 +03:00
d . v - > d_type ! = DT_SUBVOL
? le64_to_cpu ( d . v - > d_inum )
: le32_to_cpu ( d . v - > d_child_subvol ) ,
2021-10-28 23:16:55 +03:00
bch2_d_type_str ( d . v - > d_type ) ) ;
2017-03-17 09:18:50 +03:00
}
static struct bkey_i_dirent * dirent_create_key ( struct btree_trans * trans ,
2021-10-12 19:06:02 +03:00
subvol_inum dir , u8 type ,
const struct qstr * name , u64 dst )
2017-03-17 09:18:50 +03:00
{
struct bkey_i_dirent * dirent ;
unsigned u64s = BKEY_U64s + dirent_val_u64s ( name - > len ) ;
if ( name - > len > BCH_NAME_MAX )
return ERR_PTR ( - ENAMETOOLONG ) ;
BUG_ON ( u64s > U8_MAX ) ;
dirent = bch2_trans_kmalloc ( trans , u64s * sizeof ( u64 ) ) ;
if ( IS_ERR ( dirent ) )
return dirent ;
bkey_dirent_init ( & dirent - > k_i ) ;
dirent - > k . u64s = u64s ;
2021-10-12 19:06:02 +03:00
if ( type ! = DT_SUBVOL ) {
dirent - > v . d_inum = cpu_to_le64 ( dst ) ;
} else {
dirent - > v . d_parent_subvol = cpu_to_le32 ( dir . subvol ) ;
dirent - > v . d_child_subvol = cpu_to_le32 ( dst ) ;
}
2017-03-17 09:18:50 +03:00
dirent - > v . d_type = type ;
memcpy ( dirent - > v . d_name , name - > name , name - > len ) ;
memset ( dirent - > v . d_name + name - > len , 0 ,
bkey_val_bytes ( & dirent - > k ) -
offsetof ( struct bch_dirent , d_name ) -
name - > len ) ;
EBUG_ON ( bch2_dirent_name_bytes ( dirent_i_to_s_c ( dirent ) ) ! = name - > len ) ;
return dirent ;
}
2021-03-16 07:28:17 +03:00
int bch2_dirent_create ( struct btree_trans * trans , subvol_inum dir ,
const struct bch_hash_info * hash_info ,
2019-10-03 01:35:36 +03:00
u8 type , const struct qstr * name , u64 dst_inum ,
2021-03-03 02:35:30 +03:00
u64 * dir_offset , int flags )
2017-03-17 09:18:50 +03:00
{
struct bkey_i_dirent * dirent ;
int ret ;
2021-10-12 19:06:02 +03:00
dirent = dirent_create_key ( trans , dir , type , name , dst_inum ) ;
2017-03-17 09:18:50 +03:00
ret = PTR_ERR_OR_ZERO ( dirent ) ;
if ( ret )
return ret ;
2021-03-03 02:35:30 +03:00
ret = bch2_hash_set ( trans , bch2_dirent_hash_desc , hash_info ,
2021-03-16 07:28:17 +03:00
dir , & dirent - > k_i , flags ) ;
2021-03-03 02:35:30 +03:00
* dir_offset = dirent - > k . p . offset ;
return ret ;
2017-03-17 09:18:50 +03:00
}
static void dirent_copy_target ( struct bkey_i_dirent * dst ,
struct bkey_s_c_dirent src )
{
dst - > v . d_inum = src . v - > d_inum ;
dst - > v . d_type = src . v - > d_type ;
}
2021-11-14 03:49:14 +03:00
int bch2_dirent_read_target ( struct btree_trans * trans , subvol_inum dir ,
struct bkey_s_c_dirent d , subvol_inum * target )
2021-03-16 07:46:26 +03:00
{
2021-10-14 20:14:40 +03:00
struct bch_subvolume s ;
2021-03-16 07:46:26 +03:00
int ret = 0 ;
2021-10-12 19:06:02 +03:00
if ( d . v - > d_type = = DT_SUBVOL & &
d . v - > d_parent_subvol ! = dir . subvol )
return 1 ;
2021-03-16 07:46:26 +03:00
if ( likely ( d . v - > d_type ! = DT_SUBVOL ) ) {
2021-10-12 19:06:02 +03:00
target - > subvol = dir . subvol ;
target - > inum = le64_to_cpu ( d . v - > d_inum ) ;
2021-03-16 07:46:26 +03:00
} else {
2021-10-12 19:06:02 +03:00
target - > subvol = le32_to_cpu ( d . v - > d_child_subvol ) ;
2021-10-01 02:46:23 +03:00
2021-10-12 19:06:02 +03:00
ret = bch2_subvolume_get ( trans , target - > subvol , true , BTREE_ITER_CACHED , & s ) ;
2021-10-01 02:46:23 +03:00
2021-10-12 19:06:02 +03:00
target - > inum = le64_to_cpu ( s . inode ) ;
2021-03-16 07:46:26 +03:00
}
return ret ;
}
2017-03-17 09:18:50 +03:00
int bch2_dirent_rename ( struct btree_trans * trans ,
2021-03-16 07:28:17 +03:00
subvol_inum src_dir , struct bch_hash_info * src_hash ,
subvol_inum dst_dir , struct bch_hash_info * dst_hash ,
const struct qstr * src_name , subvol_inum * src_inum , u64 * src_offset ,
const struct qstr * dst_name , subvol_inum * dst_inum , u64 * dst_offset ,
enum bch_rename_mode mode )
2017-03-17 09:18:50 +03:00
{
2021-08-30 22:18:31 +03:00
struct btree_iter src_iter = { NULL } ;
struct btree_iter dst_iter = { NULL } ;
2021-12-15 00:05:47 +03:00
struct bkey_s_c old_src , old_dst = bkey_s_c_null ;
2017-03-17 09:18:50 +03:00
struct bkey_i_dirent * new_src = NULL , * new_dst = NULL ;
2019-10-03 01:35:36 +03:00
struct bpos dst_pos =
2021-03-16 07:28:17 +03:00
POS ( dst_dir . inum , bch2_dirent_hash ( dst_hash , dst_name ) ) ;
2021-10-12 19:06:02 +03:00
unsigned src_type = 0 , dst_type = 0 , src_update_flags = 0 ;
2020-02-26 23:39:46 +03:00
int ret = 0 ;
2017-03-17 09:18:50 +03:00
2021-03-16 07:28:17 +03:00
if ( src_dir . subvol ! = dst_dir . subvol )
return - EXDEV ;
memset ( src_inum , 0 , sizeof ( * src_inum ) ) ;
memset ( dst_inum , 0 , sizeof ( * dst_inum ) ) ;
2019-10-03 01:35:36 +03:00
2017-03-17 09:18:50 +03:00
/* Lookup src: */
2021-08-30 22:18:31 +03:00
ret = bch2_hash_lookup ( trans , & src_iter , bch2_dirent_hash_desc ,
src_hash , src_dir , src_name ,
BTREE_ITER_INTENT ) ;
2020-02-26 23:39:46 +03:00
if ( ret )
goto out ;
2021-08-30 22:18:31 +03:00
old_src = bch2_btree_iter_peek_slot ( & src_iter ) ;
2021-07-25 02:50:40 +03:00
ret = bkey_err ( old_src ) ;
if ( ret )
goto out ;
2021-03-16 07:28:17 +03:00
ret = bch2_dirent_read_target ( trans , src_dir ,
bkey_s_c_to_dirent ( old_src ) , src_inum ) ;
if ( ret )
goto out ;
2017-03-17 09:18:50 +03:00
2021-10-12 19:06:02 +03:00
src_type = bkey_s_c_to_dirent ( old_src ) . v - > d_type ;
if ( src_type = = DT_SUBVOL & & mode = = BCH_RENAME_EXCHANGE )
return - EOPNOTSUPP ;
/* Lookup dst: */
if ( mode = = BCH_RENAME ) {
/*
* Note that we ' re _not_ checking if the target already exists -
* we ' re relying on the VFS to do that check for us for
* correctness :
*/
ret = bch2_hash_hole ( trans , & dst_iter , bch2_dirent_hash_desc ,
dst_hash , dst_dir , dst_name ) ;
if ( ret )
goto out ;
} else {
ret = bch2_hash_lookup ( trans , & dst_iter , bch2_dirent_hash_desc ,
dst_hash , dst_dir , dst_name ,
BTREE_ITER_INTENT ) ;
if ( ret )
goto out ;
old_dst = bch2_btree_iter_peek_slot ( & dst_iter ) ;
ret = bkey_err ( old_dst ) ;
if ( ret )
goto out ;
ret = bch2_dirent_read_target ( trans , dst_dir ,
bkey_s_c_to_dirent ( old_dst ) , dst_inum ) ;
if ( ret )
goto out ;
dst_type = bkey_s_c_to_dirent ( old_dst ) . v - > d_type ;
if ( dst_type = = DT_SUBVOL )
return - EOPNOTSUPP ;
}
if ( mode ! = BCH_RENAME_EXCHANGE )
* src_offset = dst_iter . pos . offset ;
2017-03-17 09:18:50 +03:00
/* Create new dst key: */
2021-10-12 19:06:02 +03:00
new_dst = dirent_create_key ( trans , dst_dir , 0 , dst_name , 0 ) ;
2020-02-26 23:39:46 +03:00
ret = PTR_ERR_OR_ZERO ( new_dst ) ;
if ( ret )
goto out ;
2017-03-17 09:18:50 +03:00
dirent_copy_target ( new_dst , bkey_s_c_to_dirent ( old_src ) ) ;
2021-08-30 22:18:31 +03:00
new_dst - > k . p = dst_iter . pos ;
2017-03-17 09:18:50 +03:00
/* Create new src key: */
if ( mode = = BCH_RENAME_EXCHANGE ) {
2021-10-12 19:06:02 +03:00
new_src = dirent_create_key ( trans , src_dir , 0 , src_name , 0 ) ;
2020-02-26 23:39:46 +03:00
ret = PTR_ERR_OR_ZERO ( new_src ) ;
if ( ret )
goto out ;
2017-03-17 09:18:50 +03:00
dirent_copy_target ( new_src , bkey_s_c_to_dirent ( old_dst ) ) ;
2021-08-30 22:18:31 +03:00
new_src - > k . p = src_iter . pos ;
2017-03-17 09:18:50 +03:00
} else {
new_src = bch2_trans_kmalloc ( trans , sizeof ( struct bkey_i ) ) ;
2020-02-26 23:39:46 +03:00
ret = PTR_ERR_OR_ZERO ( new_src ) ;
if ( ret )
goto out ;
2017-03-17 09:18:50 +03:00
bkey_init ( & new_src - > k ) ;
2021-08-30 22:18:31 +03:00
new_src - > k . p = src_iter . pos ;
2017-03-17 09:18:50 +03:00
2021-08-30 22:18:31 +03:00
if ( bkey_cmp ( dst_pos , src_iter . pos ) < = 0 & &
bkey_cmp ( src_iter . pos , dst_iter . pos ) < 0 ) {
2017-03-17 09:18:50 +03:00
/*
* We have a hash collision for the new dst key ,
* and new_src - the key we ' re deleting - is between
* new_dst ' s hashed slot and the slot we ' re going to be
* inserting it into - oops . This will break the hash
* table if we don ' t deal with it :
*/
if ( mode = = BCH_RENAME ) {
/*
* If we ' re not overwriting , we can just insert
* new_dst at the src position :
*/
2021-10-12 19:06:02 +03:00
new_src = new_dst ;
new_src - > k . p = src_iter . pos ;
goto out_set_src ;
2017-03-17 09:18:50 +03:00
} else {
/* If we're overwriting, we can't insert new_dst
* at a different slot because it has to
* overwrite old_dst - just make sure to use a
* whiteout when deleting src :
*/
2021-02-21 03:09:53 +03:00
new_src - > k . type = KEY_TYPE_hash_whiteout ;
2017-03-17 09:18:50 +03:00
}
} else {
/* Check if we need a whiteout to delete src: */
ret = bch2_hash_needs_whiteout ( trans , bch2_dirent_hash_desc ,
2021-08-30 22:18:31 +03:00
src_hash , & src_iter ) ;
2017-03-17 09:18:50 +03:00
if ( ret < 0 )
2020-02-26 23:39:46 +03:00
goto out ;
2017-03-17 09:18:50 +03:00
if ( ret )
2021-02-21 03:09:53 +03:00
new_src - > k . type = KEY_TYPE_hash_whiteout ;
2017-03-17 09:18:50 +03:00
}
}
2021-12-05 08:30:49 +03:00
ret = bch2_trans_update ( trans , & dst_iter , & new_dst - > k_i , 0 ) ;
if ( ret )
goto out ;
2021-10-12 19:06:02 +03:00
out_set_src :
/*
* If we ' re deleting a subvolume , we need to really delete the dirent ,
* not just emit a whiteout in the current snapshot :
*/
if ( src_type = = DT_SUBVOL ) {
bch2_btree_iter_set_snapshot ( & src_iter , old_src . k - > p . snapshot ) ;
ret = bch2_btree_iter_traverse ( & src_iter ) ;
if ( ret )
goto out ;
new_src - > k . p = src_iter . pos ;
src_update_flags | = BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE ;
}
2021-12-05 08:30:49 +03:00
ret = bch2_trans_update ( trans , & src_iter , & new_src - > k_i , src_update_flags ) ;
if ( ret )
goto out ;
2021-10-12 19:06:02 +03:00
2021-05-20 07:09:47 +03:00
if ( mode = = BCH_RENAME_EXCHANGE )
* src_offset = new_src - > k . p . offset ;
2021-03-03 02:35:30 +03:00
* dst_offset = new_dst - > k . p . offset ;
2020-02-26 23:39:46 +03:00
out :
2021-08-30 22:18:31 +03:00
bch2_trans_iter_exit ( trans , & src_iter ) ;
bch2_trans_iter_exit ( trans , & dst_iter ) ;
2020-02-26 23:39:46 +03:00
return ret ;
2017-03-17 09:18:50 +03:00
}
2021-08-30 22:18:31 +03:00
int __bch2_dirent_lookup_trans ( struct btree_trans * trans ,
struct btree_iter * iter ,
2021-03-16 07:28:17 +03:00
subvol_inum dir ,
2021-08-30 22:18:31 +03:00
const struct bch_hash_info * hash_info ,
2021-03-16 07:28:17 +03:00
const struct qstr * name , subvol_inum * inum ,
2021-03-16 07:46:26 +03:00
unsigned flags )
2019-10-03 01:35:36 +03:00
{
2021-03-16 07:46:26 +03:00
struct bkey_s_c k ;
struct bkey_s_c_dirent d ;
2021-03-16 07:28:17 +03:00
u32 snapshot ;
2021-03-16 07:46:26 +03:00
int ret ;
2021-03-16 07:28:17 +03:00
ret = bch2_subvolume_get_snapshot ( trans , dir . subvol , & snapshot ) ;
if ( ret )
return ret ;
2021-03-16 07:46:26 +03:00
ret = bch2_hash_lookup ( trans , iter , bch2_dirent_hash_desc ,
2021-03-16 07:28:17 +03:00
hash_info , dir , name , flags ) ;
2021-03-16 07:46:26 +03:00
if ( ret )
return ret ;
k = bch2_btree_iter_peek_slot ( iter ) ;
ret = bkey_err ( k ) ;
2021-11-13 20:57:00 +03:00
if ( ret )
goto err ;
2021-03-16 07:46:26 +03:00
d = bkey_s_c_to_dirent ( k ) ;
2021-03-16 07:28:17 +03:00
ret = bch2_dirent_read_target ( trans , dir , d , inum ) ;
2021-10-12 19:06:02 +03:00
if ( ret > 0 )
ret = - ENOENT ;
2021-11-13 20:57:00 +03:00
err :
2021-03-16 07:46:26 +03:00
if ( ret )
bch2_trans_iter_exit ( trans , iter ) ;
return ret ;
2017-03-17 09:18:50 +03:00
}
2021-03-16 07:28:17 +03:00
u64 bch2_dirent_lookup ( struct bch_fs * c , subvol_inum dir ,
2017-03-17 09:18:50 +03:00
const struct bch_hash_info * hash_info ,
2021-03-16 07:28:17 +03:00
const struct qstr * name , subvol_inum * inum )
2017-03-17 09:18:50 +03:00
{
struct btree_trans trans ;
2021-08-30 22:18:31 +03:00
struct btree_iter iter ;
2021-03-16 07:28:17 +03:00
int ret ;
2017-03-17 09:18:50 +03:00
2019-05-15 17:54:43 +03:00
bch2_trans_init ( & trans , c , 0 , 0 ) ;
2021-03-16 07:46:26 +03:00
retry :
bch2_trans_begin ( & trans ) ;
2021-03-16 07:28:17 +03:00
ret = __bch2_dirent_lookup_trans ( & trans , & iter , dir , hash_info ,
name , inum , 0 ) ;
2021-03-16 07:46:26 +03:00
if ( ret = = - EINTR )
goto retry ;
2021-11-13 20:57:00 +03:00
if ( ! ret )
bch2_trans_iter_exit ( & trans , & iter ) ;
2017-03-17 09:18:50 +03:00
bch2_trans_exit ( & trans ) ;
2021-03-16 07:28:17 +03:00
return ret ;
2017-03-17 09:18:50 +03:00
}
2021-03-16 07:28:17 +03:00
int bch2_empty_dir_trans ( struct btree_trans * trans , subvol_inum dir )
2017-03-17 09:18:50 +03:00
{
2021-08-30 22:18:31 +03:00
struct btree_iter iter ;
2017-03-17 09:18:50 +03:00
struct bkey_s_c k ;
2021-03-16 07:28:17 +03:00
u32 snapshot ;
2019-04-17 22:49:28 +03:00
int ret ;
2019-03-25 22:10:15 +03:00
2021-03-16 07:28:17 +03:00
ret = bch2_subvolume_get_snapshot ( trans , dir . subvol , & snapshot ) ;
if ( ret )
return ret ;
2022-03-11 20:31:52 +03:00
for_each_btree_key_upto_norestart ( trans , iter , BTREE_ID_dirents ,
SPOS ( dir . inum , 0 , snapshot ) ,
POS ( dir . inum , U64_MAX ) , 0 , k , ret )
2018-11-01 22:10:01 +03:00
if ( k . k - > type = = KEY_TYPE_dirent ) {
2017-03-17 09:18:50 +03:00
ret = - ENOTEMPTY ;
break ;
}
2021-08-30 22:18:31 +03:00
bch2_trans_iter_exit ( trans , & iter ) ;
2017-03-17 09:18:50 +03:00
return ret ;
}
2021-03-16 07:28:17 +03:00
int bch2_readdir ( struct bch_fs * c , subvol_inum inum , struct dir_context * ctx )
2019-04-01 00:37:30 +03:00
{
2019-03-25 22:10:15 +03:00
struct btree_trans trans ;
2021-08-30 22:18:31 +03:00
struct btree_iter iter ;
2017-03-17 09:18:50 +03:00
struct bkey_s_c k ;
struct bkey_s_c_dirent dirent ;
2021-10-12 19:06:02 +03:00
subvol_inum target ;
2021-03-16 07:28:17 +03:00
u32 snapshot ;
2019-04-17 22:49:28 +03:00
int ret ;
2017-03-17 09:18:50 +03:00
2019-05-15 17:54:43 +03:00
bch2_trans_init ( & trans , c , 0 , 0 ) ;
2021-03-16 07:28:17 +03:00
retry :
bch2_trans_begin ( & trans ) ;
ret = bch2_subvolume_get_snapshot ( & trans , inum . subvol , & snapshot ) ;
if ( ret )
goto err ;
2019-03-25 22:10:15 +03:00
2022-03-11 20:31:52 +03:00
for_each_btree_key_upto_norestart ( & trans , iter , BTREE_ID_dirents ,
SPOS ( inum . inum , ctx - > pos , snapshot ) ,
POS ( inum . inum , U64_MAX ) , 0 , k , ret ) {
2018-11-01 22:10:01 +03:00
if ( k . k - > type ! = KEY_TYPE_dirent )
2017-03-17 09:18:50 +03:00
continue ;
dirent = bkey_s_c_to_dirent ( k ) ;
2021-10-12 19:06:02 +03:00
ret = bch2_dirent_read_target ( & trans , inum , dirent , & target ) ;
if ( ret < 0 )
break ;
if ( ret )
continue ;
2017-03-17 09:18:50 +03:00
/*
* XXX : dir_emit ( ) can fault and block , while we ' re holding
* locks
*/
2019-10-09 16:23:30 +03:00
ctx - > pos = dirent . k - > p . offset ;
if ( ! dir_emit ( ctx , dirent . v - > d_name ,
bch2_dirent_name_bytes ( dirent ) ,
2021-10-12 19:06:02 +03:00
target . inum ,
2021-03-16 07:46:26 +03:00
vfs_d_type ( dirent . v - > d_type ) ) )
2017-03-17 09:18:50 +03:00
break ;
2019-10-09 16:23:30 +03:00
ctx - > pos = dirent . k - > p . offset + 1 ;
2021-10-23 00:33:38 +03:00
/*
* read_target looks up subvolumes , we can overflow paths if the
* directory has many subvolumes in it
*/
2021-11-24 03:00:23 +03:00
ret = btree_trans_too_many_iters ( & trans ) ;
if ( ret )
2021-10-23 00:33:38 +03:00
break ;
2017-03-17 09:18:50 +03:00
}
2021-08-30 22:18:31 +03:00
bch2_trans_iter_exit ( & trans , & iter ) ;
2021-03-16 07:28:17 +03:00
err :
if ( ret = = - EINTR )
goto retry ;
2021-03-20 03:29:11 +03:00
2021-10-19 22:08:00 +03:00
bch2_trans_exit ( & trans ) ;
2017-03-17 09:18:50 +03:00
2019-04-17 22:49:28 +03:00
return ret ;
2017-03-17 09:18:50 +03:00
}