2017-03-16 22:18:50 -08:00
// SPDX-License-Identifier: GPL-2.0
# include "bcachefs.h"
2020-12-17 15:08:58 -05:00
# include "bkey_buf.h"
2017-03-16 22:18:50 -08:00
# include "btree_update.h"
2022-03-29 15:48:45 -04:00
# include "darray.h"
2017-03-16 22:18:50 -08:00
# include "dirent.h"
# include "error.h"
2019-10-02 18:35:36 -04:00
# include "fs-common.h"
2017-03-16 22:18:50 -08:00
# include "fsck.h"
# include "inode.h"
# include "keylist.h"
2021-03-16 00:42:25 -04:00
# include "subvolume.h"
2017-03-16 22:18:50 -08:00
# include "super.h"
# include "xattr.h"
2021-04-21 21:08:49 -04:00
# include <linux/bsearch.h>
2017-03-16 22:18:50 -08:00
# include <linux/dcache.h> /* struct qstr */
# define QSTR(n) { { { .len = strlen(n) } }, .name = n }
2021-04-20 00:15:44 -04:00
static s64 bch2_count_inode_sectors ( struct btree_trans * trans , u64 inum ,
u32 snapshot )
2019-03-25 15:10:15 -04:00
{
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2019-03-25 15:10:15 -04:00
struct bkey_s_c k ;
u64 sectors = 0 ;
2019-04-17 15:49:28 -04:00
int ret ;
2019-03-25 15:10:15 -04:00
2021-02-20 19:27:37 -05:00
for_each_btree_key ( trans , iter , BTREE_ID_extents ,
2021-04-20 00:15:44 -04:00
SPOS ( inum , 0 , snapshot ) , 0 , k , ret ) {
2019-03-25 15:10:15 -04:00
if ( k . k - > p . inode ! = inum )
break ;
if ( bkey_extent_is_allocation ( k . k ) )
sectors + = k . k - > size ;
}
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( trans , & iter ) ;
2019-04-17 15:49:28 -04:00
return ret ? : sectors ;
2019-03-25 15:10:15 -04:00
}
2021-04-20 00:15:44 -04:00
static s64 bch2_count_subdirs ( struct btree_trans * trans , u64 inum ,
u32 snapshot )
{
struct btree_iter iter ;
struct bkey_s_c k ;
struct bkey_s_c_dirent d ;
u64 subdirs = 0 ;
int ret ;
for_each_btree_key ( trans , iter , BTREE_ID_dirents ,
SPOS ( inum , 0 , snapshot ) , 0 , k , ret ) {
if ( k . k - > p . inode ! = inum )
break ;
if ( k . k - > type ! = KEY_TYPE_dirent )
continue ;
d = bkey_s_c_to_dirent ( k ) ;
if ( d . v - > d_type = = DT_DIR )
subdirs + + ;
}
bch2_trans_iter_exit ( trans , & iter ) ;
return ret ? : subdirs ;
}
2021-04-19 23:31:40 -04:00
static int __snapshot_lookup_subvol ( struct btree_trans * trans , u32 snapshot ,
u32 * subvol )
{
struct btree_iter iter ;
struct bkey_s_c k ;
int ret ;
bch2_trans_iter_init ( trans , & iter , BTREE_ID_snapshots ,
POS ( 0 , snapshot ) , 0 ) ;
k = bch2_btree_iter_peek_slot ( & iter ) ;
ret = bkey_err ( k ) ;
if ( ret )
goto err ;
if ( k . k - > type ! = KEY_TYPE_snapshot ) {
bch_err ( trans - > c , " snapshot %u not fonud " , snapshot ) ;
ret = - ENOENT ;
goto err ;
}
* subvol = le32_to_cpu ( bkey_s_c_to_snapshot ( k ) . v - > subvol ) ;
err :
bch2_trans_iter_exit ( trans , & iter ) ;
return ret ;
}
2021-04-20 00:15:44 -04:00
static int __subvol_lookup ( struct btree_trans * trans , u32 subvol ,
u32 * snapshot , u64 * inum )
2021-04-19 23:31:40 -04:00
{
2021-09-30 19:46:23 -04:00
struct bch_subvolume s ;
2021-04-19 23:31:40 -04:00
int ret ;
2021-09-30 19:46:23 -04:00
ret = bch2_subvolume_get ( trans , subvol , false , 0 , & s ) ;
2021-04-19 23:31:40 -04:00
2021-09-30 19:46:23 -04:00
* snapshot = le32_to_cpu ( s . snapshot ) ;
* inum = le64_to_cpu ( s . inode ) ;
2021-04-19 23:31:40 -04:00
return ret ;
}
2021-04-20 00:15:44 -04:00
static int subvol_lookup ( struct btree_trans * trans , u32 subvol ,
u32 * snapshot , u64 * inum )
2021-04-19 23:31:40 -04:00
{
2021-04-20 00:15:44 -04:00
return lockrestart_do ( trans , __subvol_lookup ( trans , subvol , snapshot , inum ) ) ;
2021-04-19 23:31:40 -04:00
}
2021-11-03 21:22:46 -04:00
static int lookup_first_inode ( struct btree_trans * trans , u64 inode_nr ,
struct bch_inode_unpacked * inode )
{
struct btree_iter iter ;
struct bkey_s_c k ;
int ret ;
bch2_trans_iter_init ( trans , & iter , BTREE_ID_inodes ,
POS ( 0 , inode_nr ) ,
BTREE_ITER_ALL_SNAPSHOTS ) ;
k = bch2_btree_iter_peek ( & iter ) ;
ret = bkey_err ( k ) ;
if ( ret )
goto err ;
if ( ! k . k | | bkey_cmp ( k . k - > p , POS ( 0 , inode_nr ) ) ) {
ret = - ENOENT ;
goto err ;
}
2021-10-29 21:14:23 -04:00
ret = bch2_inode_unpack ( k , inode ) ;
2021-11-03 21:22:46 -04:00
err :
if ( ret & & ret ! = - EINTR )
bch_err ( trans - > c , " error %i fetching inode %llu " ,
ret , inode_nr ) ;
bch2_trans_iter_exit ( trans , & iter ) ;
return ret ;
}
2021-04-19 22:19:18 -04:00
static int __lookup_inode ( struct btree_trans * trans , u64 inode_nr ,
struct bch_inode_unpacked * inode ,
u32 * snapshot )
2021-04-06 20:15:26 -04:00
{
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2021-04-06 20:15:26 -04:00
struct bkey_s_c k ;
int ret ;
2021-08-30 15:18:31 -04:00
bch2_trans_iter_init ( trans , & iter , BTREE_ID_inodes ,
2021-04-20 00:15:44 -04:00
SPOS ( 0 , inode_nr , * snapshot ) , 0 ) ;
2021-08-30 15:18:31 -04:00
k = bch2_btree_iter_peek_slot ( & iter ) ;
2021-04-06 20:15:26 -04:00
ret = bkey_err ( k ) ;
if ( ret )
goto err ;
2021-10-29 21:14:23 -04:00
ret = bkey_is_inode ( k . k )
? bch2_inode_unpack ( k , inode )
2021-04-06 20:15:26 -04:00
: - ENOENT ;
2021-10-12 12:06:02 -04:00
if ( ! ret )
* snapshot = iter . pos . snapshot ;
2021-04-06 20:15:26 -04:00
err :
2021-10-28 16:16:55 -04:00
if ( ret & & ret ! = - EINTR )
bch_err ( trans - > c , " error %i fetching inode %llu:%u " ,
ret , inode_nr , * snapshot ) ;
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( trans , & iter ) ;
2021-04-06 20:15:26 -04:00
return ret ;
}
2021-04-19 22:19:18 -04:00
static int lookup_inode ( struct btree_trans * trans , u64 inode_nr ,
struct bch_inode_unpacked * inode ,
u32 * snapshot )
{
return lockrestart_do ( trans , __lookup_inode ( trans , inode_nr , inode , snapshot ) ) ;
}
2021-04-20 00:15:44 -04:00
static int __lookup_dirent ( struct btree_trans * trans ,
struct bch_hash_info hash_info ,
subvol_inum dir , struct qstr * name ,
u64 * target , unsigned * type )
{
struct btree_iter iter ;
struct bkey_s_c_dirent d ;
int ret ;
ret = bch2_hash_lookup ( trans , & iter , bch2_dirent_hash_desc ,
& hash_info , dir , name , 0 ) ;
if ( ret )
return ret ;
d = bkey_s_c_to_dirent ( bch2_btree_iter_peek_slot ( & iter ) ) ;
* target = le64_to_cpu ( d . v - > d_inum ) ;
* type = d . v - > d_type ;
bch2_trans_iter_exit ( trans , & iter ) ;
return 0 ;
}
2021-04-19 22:19:18 -04:00
static int __write_inode ( struct btree_trans * trans ,
struct bch_inode_unpacked * inode ,
u32 snapshot )
2021-04-06 20:15:26 -04:00
{
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
int ret ;
bch2_trans_iter_init ( trans , & iter , BTREE_ID_inodes ,
SPOS ( 0 , inode - > bi_inum , snapshot ) ,
BTREE_ITER_INTENT ) ;
ret = bch2_btree_iter_traverse ( & iter ) ? :
bch2_inode_write ( trans , & iter , inode ) ;
bch2_trans_iter_exit ( trans , & iter ) ;
2021-04-19 22:19:18 -04:00
return ret ;
}
static int write_inode ( struct btree_trans * trans ,
struct bch_inode_unpacked * inode ,
u32 snapshot )
{
2022-07-13 05:25:29 -04:00
int ret = commit_do ( trans , NULL , NULL ,
2021-04-06 20:15:26 -04:00
BTREE_INSERT_NOFAIL |
BTREE_INSERT_LAZY_RW ,
2021-04-19 22:19:18 -04:00
__write_inode ( trans , inode , snapshot ) ) ;
2021-04-06 20:15:26 -04:00
if ( ret )
bch_err ( trans - > c , " error in fsck: error %i updating inode " , ret ) ;
return ret ;
}
2021-04-20 00:15:44 -04:00
static int fsck_inode_rm ( struct btree_trans * trans , u64 inum , u32 snapshot )
{
2022-07-14 00:44:09 -04:00
struct bch_fs * c = trans - > c ;
2021-04-20 00:15:44 -04:00
struct btree_iter iter = { NULL } ;
struct bkey_i_inode_generation delete ;
struct bch_inode_unpacked inode_u ;
struct bkey_s_c k ;
int ret ;
ret = bch2_btree_delete_range_trans ( trans , BTREE_ID_extents ,
SPOS ( inum , 0 , snapshot ) ,
SPOS ( inum , U64_MAX , snapshot ) ,
0 , NULL ) ? :
bch2_btree_delete_range_trans ( trans , BTREE_ID_dirents ,
SPOS ( inum , 0 , snapshot ) ,
SPOS ( inum , U64_MAX , snapshot ) ,
0 , NULL ) ? :
bch2_btree_delete_range_trans ( trans , BTREE_ID_xattrs ,
SPOS ( inum , 0 , snapshot ) ,
SPOS ( inum , U64_MAX , snapshot ) ,
0 , NULL ) ;
if ( ret )
goto err ;
retry :
bch2_trans_begin ( trans ) ;
bch2_trans_iter_init ( trans , & iter , BTREE_ID_inodes ,
SPOS ( 0 , inum , snapshot ) , BTREE_ITER_INTENT ) ;
k = bch2_btree_iter_peek_slot ( & iter ) ;
ret = bkey_err ( k ) ;
if ( ret )
goto err ;
2021-10-29 21:14:23 -04:00
if ( ! bkey_is_inode ( k . k ) ) {
2022-07-14 00:44:09 -04:00
bch2_fs_inconsistent ( c ,
2021-04-20 00:15:44 -04:00
" inode %llu:%u not found when deleting " ,
inum , snapshot ) ;
ret = - EIO ;
goto err ;
}
2021-10-29 21:14:23 -04:00
bch2_inode_unpack ( k , & inode_u ) ;
2021-04-20 00:15:44 -04:00
/* Subvolume root? */
2022-07-14 00:44:09 -04:00
if ( inode_u . bi_subvol )
bch_warn ( c , " deleting inode %llu marked as unlinked, but also a subvolume root!? " , inode_u . bi_inum ) ;
2021-04-20 00:15:44 -04:00
bkey_inode_generation_init ( & delete . k_i ) ;
delete . k . p = iter . pos ;
delete . v . bi_generation = cpu_to_le32 ( inode_u . bi_generation + 1 ) ;
ret = bch2_trans_update ( trans , & iter , & delete . k_i , 0 ) ? :
bch2_trans_commit ( trans , NULL , NULL ,
BTREE_INSERT_NOFAIL ) ;
err :
bch2_trans_iter_exit ( trans , & iter ) ;
if ( ret = = - EINTR )
goto retry ;
return ret ;
}
2021-04-16 14:48:51 -04:00
static int __remove_dirent ( struct btree_trans * trans , struct bpos pos )
2017-03-16 22:18:50 -08:00
{
2019-03-27 22:03:30 -04:00
struct bch_fs * c = trans - > c ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2017-03-16 22:18:50 -08:00
struct bch_inode_unpacked dir_inode ;
struct bch_hash_info dir_hash_info ;
int ret ;
2021-11-03 21:22:46 -04:00
ret = lookup_first_inode ( trans , pos . inode , & dir_inode ) ;
2019-12-22 23:04:30 -05:00
if ( ret )
2022-04-11 22:36:53 -04:00
goto err ;
2017-03-16 22:18:50 -08:00
dir_hash_info = bch2_hash_info_init ( c , & dir_inode ) ;
2021-08-30 15:18:31 -04:00
bch2_trans_iter_init ( trans , & iter , BTREE_ID_dirents , pos , BTREE_ITER_INTENT ) ;
2019-12-22 23:04:30 -05:00
2021-04-16 14:48:51 -04:00
ret = bch2_hash_delete_at ( trans , bch2_dirent_hash_desc ,
2022-04-12 13:09:09 -04:00
& dir_hash_info , & iter ,
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE ) ;
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( trans , & iter ) ;
2022-04-11 22:36:53 -04:00
err :
if ( ret & & ret ! = - EINTR )
bch_err ( c , " error %i from __remove_dirent() " , ret ) ;
2021-04-16 14:48:51 -04:00
return ret ;
2019-12-22 23:04:30 -05:00
}
2021-04-19 22:19:18 -04:00
/* Get lost+found, create if it doesn't exist: */
2021-04-20 00:15:44 -04:00
static int lookup_lostfound ( struct btree_trans * trans , u32 subvol ,
2021-04-19 22:19:18 -04:00
struct bch_inode_unpacked * lostfound )
2017-03-16 22:18:50 -08:00
{
2021-04-19 22:19:18 -04:00
struct bch_fs * c = trans - > c ;
struct bch_inode_unpacked root ;
struct bch_hash_info root_hash_info ;
struct qstr lostfound_str = QSTR ( " lost+found " ) ;
2021-04-20 00:15:44 -04:00
subvol_inum root_inum = { . subvol = subvol } ;
u64 inum = 0 ;
unsigned d_type = 0 ;
2021-04-19 22:19:18 -04:00
u32 snapshot ;
int ret ;
2021-10-28 16:16:55 -04:00
ret = __subvol_lookup ( trans , subvol , & snapshot , & root_inum . inum ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
return ret ;
2021-04-19 23:31:40 -04:00
2021-10-28 16:16:55 -04:00
ret = __lookup_inode ( trans , root_inum . inum , & root , & snapshot ) ;
if ( ret )
2021-04-19 22:19:18 -04:00
return ret ;
root_hash_info = bch2_hash_info_init ( c , & root ) ;
2021-04-20 00:15:44 -04:00
2021-10-28 16:16:55 -04:00
ret = __lookup_dirent ( trans , root_hash_info , root_inum ,
2021-04-20 00:15:44 -04:00
& lostfound_str , & inum , & d_type ) ;
if ( ret = = - ENOENT ) {
2021-04-19 22:19:18 -04:00
bch_notice ( c , " creating lost+found " ) ;
goto create_lostfound ;
}
2021-10-28 16:16:55 -04:00
if ( ret & & ret ! = - EINTR )
2021-04-20 00:15:44 -04:00
bch_err ( c , " error looking up lost+found: %i " , ret ) ;
2021-10-28 16:16:55 -04:00
if ( ret )
2021-04-20 00:15:44 -04:00
return ret ;
if ( d_type ! = DT_DIR ) {
bch_err ( c , " error looking up lost+found: not a directory " ) ;
return ret ;
}
2021-10-28 16:16:55 -04:00
/*
* The check_dirents pass has already run , dangling dirents
* shouldn ' t exist here :
*/
return __lookup_inode ( trans , inum , lostfound , & snapshot ) ;
2021-04-19 22:19:18 -04:00
create_lostfound :
2021-10-28 16:16:55 -04:00
bch2_inode_init_early ( c , lostfound ) ;
ret = bch2_create_trans ( trans , root_inum , & root ,
lostfound , & lostfound_str ,
0 , 0 , S_IFDIR | 0700 , 0 , NULL , NULL ,
( subvol_inum ) { } , 0 ) ;
if ( ret & & ret ! = - EINTR )
bch_err ( c , " error creating lost+found: %i " , ret ) ;
return ret ;
2021-04-19 22:19:18 -04:00
}
2021-10-28 16:16:55 -04:00
static int __reattach_inode ( struct btree_trans * trans ,
2021-04-19 23:31:40 -04:00
struct bch_inode_unpacked * inode ,
2021-04-20 00:15:44 -04:00
u32 inode_snapshot )
2021-04-19 22:19:18 -04:00
{
struct bch_hash_info dir_hash ;
struct bch_inode_unpacked lostfound ;
2017-03-16 22:18:50 -08:00
char name_buf [ 20 ] ;
struct qstr name ;
2021-04-09 03:25:37 -04:00
u64 dir_offset = 0 ;
2021-04-19 23:31:40 -04:00
u32 subvol ;
2017-03-16 22:18:50 -08:00
int ret ;
2021-10-28 16:16:55 -04:00
ret = __snapshot_lookup_subvol ( trans , inode_snapshot , & subvol ) ;
2021-04-19 23:31:40 -04:00
if ( ret )
return ret ;
ret = lookup_lostfound ( trans , subvol , & lostfound ) ;
2021-04-09 03:25:37 -04:00
if ( ret )
2021-04-07 03:11:07 -04:00
return ret ;
2021-04-09 03:25:37 -04:00
2021-04-19 22:19:18 -04:00
if ( S_ISDIR ( inode - > bi_mode ) ) {
lostfound . bi_nlink + + ;
2021-04-09 03:25:37 -04:00
2021-10-28 16:16:55 -04:00
ret = __write_inode ( trans , & lostfound , U32_MAX ) ;
2021-04-09 03:25:37 -04:00
if ( ret )
2021-04-07 03:11:07 -04:00
return ret ;
2021-04-09 03:25:37 -04:00
}
2021-04-19 22:19:18 -04:00
dir_hash = bch2_hash_info_init ( trans - > c , & lostfound ) ;
2021-04-09 03:25:37 -04:00
2021-04-19 22:19:18 -04:00
snprintf ( name_buf , sizeof ( name_buf ) , " %llu " , inode - > bi_inum ) ;
name = ( struct qstr ) QSTR ( name_buf ) ;
2021-04-09 03:25:37 -04:00
2021-10-28 16:16:55 -04:00
ret = bch2_dirent_create ( trans ,
( subvol_inum ) {
. subvol = subvol ,
. inum = lostfound . bi_inum ,
} ,
& dir_hash ,
inode_d_type ( inode ) ,
& name , inode - > bi_inum , & dir_offset ,
BCH_HASH_SET_MUST_CREATE ) ;
if ( ret )
return ret ;
inode - > bi_dir = lostfound . bi_inum ;
inode - > bi_dir_offset = dir_offset ;
return __write_inode ( trans , inode , inode_snapshot ) ;
}
static int reattach_inode ( struct btree_trans * trans ,
struct bch_inode_unpacked * inode ,
u32 inode_snapshot )
{
2022-07-13 05:25:29 -04:00
int ret = commit_do ( trans , NULL , NULL ,
2021-10-28 16:16:55 -04:00
BTREE_INSERT_LAZY_RW |
BTREE_INSERT_NOFAIL ,
__reattach_inode ( trans , inode , inode_snapshot ) ) ;
2021-04-19 22:19:18 -04:00
if ( ret ) {
bch_err ( trans - > c , " error %i reattaching inode %llu " ,
ret , inode - > bi_inum ) ;
return ret ;
}
2021-04-09 03:25:37 -04:00
2021-10-28 16:16:55 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
}
2021-04-07 03:11:07 -04:00
static int remove_backpointer ( struct btree_trans * trans ,
struct bch_inode_unpacked * inode )
{
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2021-04-07 03:11:07 -04:00
struct bkey_s_c k ;
int ret ;
2021-08-30 15:18:31 -04:00
bch2_trans_iter_init ( trans , & iter , BTREE_ID_dirents ,
POS ( inode - > bi_dir , inode - > bi_dir_offset ) , 0 ) ;
k = bch2_btree_iter_peek_slot ( & iter ) ;
2021-04-07 03:11:07 -04:00
ret = bkey_err ( k ) ;
if ( ret )
goto out ;
if ( k . k - > type ! = KEY_TYPE_dirent ) {
ret = - ENOENT ;
goto out ;
}
2021-10-28 16:16:55 -04:00
ret = __remove_dirent ( trans , k . k - > p ) ;
2021-04-07 03:11:07 -04:00
out :
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( trans , & iter ) ;
2021-04-07 03:11:07 -04:00
return ret ;
}
2022-07-14 02:47:36 -04:00
struct snapshots_seen_entry {
u32 id ;
u32 equiv ;
} ;
struct snapshots_seen {
struct bpos pos ;
DARRAY ( struct snapshots_seen_entry ) ids ;
} ;
static inline void snapshots_seen_exit ( struct snapshots_seen * s )
{
darray_exit ( & s - > ids ) ;
}
static inline void snapshots_seen_init ( struct snapshots_seen * s )
{
memset ( s , 0 , sizeof ( * s ) ) ;
}
2022-07-16 23:31:28 -04:00
static int snapshots_seen_add ( struct bch_fs * c , struct snapshots_seen * s , u32 id )
{
struct snapshots_seen_entry * i , n = { id , id } ;
int ret ;
darray_for_each ( s - > ids , i ) {
if ( n . equiv < i - > equiv )
break ;
if ( i - > equiv = = n . equiv ) {
bch_err ( c , " adding duplicate snapshot in snapshots_seen_add() " ) ;
return - EINVAL ;
}
}
ret = darray_insert_item ( & s - > ids , i - s - > ids . data , n ) ;
if ( ret )
bch_err ( c , " error reallocating snapshots_seen table (size %zu) " ,
s - > ids . size ) ;
return ret ;
}
2022-07-14 02:47:36 -04:00
static int snapshots_seen_update ( struct bch_fs * c , struct snapshots_seen * s ,
enum btree_id btree_id , struct bpos pos )
2021-04-20 00:15:44 -04:00
{
2022-07-14 02:47:36 -04:00
struct snapshots_seen_entry * i , n = {
. id = pos . snapshot ,
. equiv = bch2_snapshot_equiv ( c , pos . snapshot ) ,
} ;
int ret ;
2021-04-20 00:15:44 -04:00
if ( bkey_cmp ( s - > pos , pos ) )
2022-03-29 15:48:45 -04:00
s - > ids . nr = 0 ;
2022-07-14 02:47:36 -04:00
pos . snapshot = n . equiv ;
2021-04-20 00:15:44 -04:00
s - > pos = pos ;
2022-07-14 02:47:36 -04:00
darray_for_each ( s - > ids , i )
if ( i - > equiv = = n . equiv ) {
if ( i - > id ! = n . id ) {
bch_err ( c , " snapshot deletion did not run correctly: \n "
" duplicate keys in btree %s at %llu:%llu snapshots %u, %u (equiv %u) \n " ,
bch2_btree_ids [ btree_id ] ,
pos . inode , pos . offset ,
i - > id , n . id , n . equiv ) ;
2022-07-16 23:21:15 -04:00
return - NEED_SNAPSHOT_CLEANUP ;
2022-07-14 02:47:36 -04:00
}
2021-04-20 00:15:44 -04:00
2022-07-14 02:47:36 -04:00
return 0 ;
}
ret = darray_push ( & s - > ids , n ) ;
if ( ret )
bch_err ( c , " error reallocating snapshots_seen table (size %zu) " ,
s - > ids . size ) ;
return ret ;
2021-04-20 00:15:44 -04:00
}
/**
* key_visible_in_snapshot - returns true if @ id is a descendent of @ ancestor ,
* and @ ancestor hasn ' t been overwritten in @ seen
*
* That is , returns whether key in @ ancestor snapshot is visible in @ id snapshot
*/
static bool key_visible_in_snapshot ( struct bch_fs * c , struct snapshots_seen * seen ,
u32 id , u32 ancestor )
{
ssize_t i ;
2022-07-14 02:47:36 -04:00
u32 top = seen - > ids . nr ? seen - > ids . data [ seen - > ids . nr - 1 ] . equiv : 0 ;
2021-04-20 00:15:44 -04:00
BUG_ON ( id > ancestor ) ;
2022-07-14 02:47:36 -04:00
BUG_ON ( ! bch2_snapshot_is_equiv ( c , id ) ) ;
BUG_ON ( ! bch2_snapshot_is_equiv ( c , ancestor ) ) ;
2021-04-20 00:15:44 -04:00
/* @ancestor should be the snapshot most recently added to @seen */
2022-07-14 02:47:36 -04:00
BUG_ON ( ancestor ! = seen - > pos . snapshot ) ;
BUG_ON ( ancestor ! = top ) ;
2021-04-20 00:15:44 -04:00
if ( id = = ancestor )
return true ;
if ( ! bch2_snapshot_is_ancestor ( c , id , ancestor ) )
return false ;
2022-03-29 15:48:45 -04:00
for ( i = seen - > ids . nr - 2 ;
2022-07-14 02:47:36 -04:00
i > = 0 & & seen - > ids . data [ i ] . equiv > = id ;
2021-04-20 00:15:44 -04:00
- - i )
2022-07-14 02:47:36 -04:00
if ( bch2_snapshot_is_ancestor ( c , id , seen - > ids . data [ i ] . equiv ) & &
bch2_snapshot_is_ancestor ( c , seen - > ids . data [ i ] . equiv , ancestor ) )
2021-04-20 00:15:44 -04:00
return false ;
return true ;
}
/**
* ref_visible - given a key with snapshot id @ src that points to a key with
* snapshot id @ dst , test whether there is some snapshot in which @ dst is
* visible .
*
* This assumes we ' re visiting @ src keys in natural key order .
*
* @ s - list of snapshot IDs already seen at @ src
* @ src - snapshot ID of src key
* @ dst - snapshot ID of dst key
*/
static int ref_visible ( struct bch_fs * c , struct snapshots_seen * s ,
u32 src , u32 dst )
{
return dst < = src
? key_visible_in_snapshot ( c , s , dst , src )
: bch2_snapshot_is_ancestor ( c , src , dst ) ;
}
2022-07-14 02:47:36 -04:00
# define for_each_visible_inode(_c, _s, _w, _snapshot, _i) \
for ( _i = ( _w ) - > inodes . data ; _i < ( _w ) - > inodes . data + ( _w ) - > inodes . nr & & \
( _i ) - > snapshot < = ( _snapshot ) ; _i + + ) \
2021-04-20 00:15:44 -04:00
if ( key_visible_in_snapshot ( _c , _s , _i - > snapshot , _snapshot ) )
2022-03-29 15:48:45 -04:00
struct inode_walker_entry {
struct bch_inode_unpacked inode ;
u32 snapshot ;
u64 count ;
} ;
2017-03-16 22:18:50 -08:00
struct inode_walker {
2021-04-20 00:15:44 -04:00
bool first_this_inode ;
u64 cur_inum ;
2022-03-29 15:48:45 -04:00
DARRAY ( struct inode_walker_entry ) inodes ;
2017-03-16 22:18:50 -08:00
} ;
2021-04-20 00:15:44 -04:00
static void inode_walker_exit ( struct inode_walker * w )
{
2022-03-29 15:48:45 -04:00
darray_exit ( & w - > inodes ) ;
2021-04-20 00:15:44 -04:00
}
2017-03-16 22:18:50 -08:00
static struct inode_walker inode_walker_init ( void )
{
2021-04-20 00:15:44 -04:00
return ( struct inode_walker ) { 0 , } ;
}
static int add_inode ( struct bch_fs * c , struct inode_walker * w ,
2021-10-29 21:14:23 -04:00
struct bkey_s_c inode )
2021-04-20 00:15:44 -04:00
{
struct bch_inode_unpacked u ;
BUG_ON ( bch2_inode_unpack ( inode , & u ) ) ;
2022-03-29 15:48:45 -04:00
return darray_push ( & w - > inodes , ( ( struct inode_walker_entry ) {
2021-04-20 00:15:44 -04:00
. inode = u ,
2022-07-14 02:47:36 -04:00
. snapshot = bch2_snapshot_equiv ( c , inode . k - > p . snapshot ) ,
2022-03-29 15:48:45 -04:00
} ) ) ;
2017-03-16 22:18:50 -08:00
}
2021-07-14 20:28:27 -04:00
static int __walk_inode ( struct btree_trans * trans ,
2021-04-20 00:15:44 -04:00
struct inode_walker * w , struct bpos pos )
2017-03-16 22:18:50 -08:00
{
2021-04-20 00:15:44 -04:00
struct bch_fs * c = trans - > c ;
struct btree_iter iter ;
struct bkey_s_c k ;
2022-07-14 02:47:36 -04:00
unsigned i ;
2021-04-20 00:15:44 -04:00
int ret ;
2017-03-16 22:18:50 -08:00
2022-07-14 02:47:36 -04:00
pos . snapshot = bch2_snapshot_equiv ( c , pos . snapshot ) ;
2017-03-16 22:18:50 -08:00
2021-04-20 00:15:44 -04:00
if ( pos . inode = = w - > cur_inum ) {
2019-03-31 17:37:30 -04:00
w - > first_this_inode = false ;
2021-04-20 00:15:44 -04:00
goto lookup_snapshot ;
2017-03-16 22:18:50 -08:00
}
2022-03-29 15:48:45 -04:00
w - > inodes . nr = 0 ;
2021-04-20 00:15:44 -04:00
for_each_btree_key ( trans , iter , BTREE_ID_inodes , POS ( 0 , pos . inode ) ,
BTREE_ITER_ALL_SNAPSHOTS , k , ret ) {
if ( k . k - > p . offset ! = pos . inode )
break ;
2021-10-29 21:14:23 -04:00
if ( bkey_is_inode ( k . k ) )
add_inode ( c , w , k ) ;
2021-04-20 00:15:44 -04:00
}
bch2_trans_iter_exit ( trans , & iter ) ;
if ( ret )
return ret ;
w - > cur_inum = pos . inode ;
w - > first_this_inode = true ;
lookup_snapshot :
2022-03-29 15:48:45 -04:00
for ( i = 0 ; i < w - > inodes . nr ; i + + )
if ( bch2_snapshot_is_ancestor ( c , pos . snapshot , w - > inodes . data [ i ] . snapshot ) )
2021-04-20 00:15:44 -04:00
goto found ;
return INT_MAX ;
found :
2022-03-29 15:48:45 -04:00
BUG_ON ( pos . snapshot > w - > inodes . data [ i ] . snapshot ) ;
2021-04-20 00:15:44 -04:00
2022-03-29 15:48:45 -04:00
if ( pos . snapshot ! = w - > inodes . data [ i ] . snapshot ) {
2022-07-14 02:47:36 -04:00
struct inode_walker_entry e = w - > inodes . data [ i ] ;
e . snapshot = pos . snapshot ;
e . count = 0 ;
bch_info ( c , " have key for inode %llu:%u but have inode in ancestor snapshot %u " ,
pos . inode , pos . snapshot , w - > inodes . data [ i ] . snapshot ) ;
2021-04-20 00:15:44 -04:00
2022-03-29 15:48:45 -04:00
while ( i & & w - > inodes . data [ i - 1 ] . snapshot > pos . snapshot )
2021-04-20 00:15:44 -04:00
- - i ;
2022-07-14 02:47:36 -04:00
ret = darray_insert_item ( & w - > inodes , i , e ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
return ret ;
}
return i ;
2017-03-16 22:18:50 -08:00
}
2021-04-20 00:15:44 -04:00
static int __get_visible_inodes ( struct btree_trans * trans ,
struct inode_walker * w ,
struct snapshots_seen * s ,
u64 inum )
{
struct bch_fs * c = trans - > c ;
struct btree_iter iter ;
struct bkey_s_c k ;
int ret ;
2022-03-29 15:48:45 -04:00
w - > inodes . nr = 0 ;
2021-04-20 00:15:44 -04:00
for_each_btree_key ( trans , iter , BTREE_ID_inodes , POS ( 0 , inum ) ,
BTREE_ITER_ALL_SNAPSHOTS , k , ret ) {
2022-07-14 02:47:36 -04:00
u32 equiv = bch2_snapshot_equiv ( c , k . k - > p . snapshot ) ;
2021-04-20 00:15:44 -04:00
if ( k . k - > p . offset ! = inum )
break ;
2022-07-14 02:47:36 -04:00
if ( ! ref_visible ( c , s , s - > pos . snapshot , equiv ) )
2021-04-20 00:15:44 -04:00
continue ;
2022-07-14 02:47:36 -04:00
if ( bkey_is_inode ( k . k ) )
2021-10-29 21:14:23 -04:00
add_inode ( c , w , k ) ;
2022-07-14 02:47:36 -04:00
if ( equiv > = s - > pos . snapshot )
break ;
2021-04-20 00:15:44 -04:00
}
bch2_trans_iter_exit ( trans , & iter ) ;
return ret ;
}
static int check_key_has_snapshot ( struct btree_trans * trans ,
struct btree_iter * iter ,
struct bkey_s_c k )
{
struct bch_fs * c = trans - > c ;
2022-02-25 13:18:19 -05:00
struct printbuf buf = PRINTBUF ;
2021-04-20 00:15:44 -04:00
int ret = 0 ;
2022-07-14 02:47:36 -04:00
if ( mustfix_fsck_err_on ( ! bch2_snapshot_equiv ( c , k . k - > p . snapshot ) , c ,
2021-04-20 00:15:44 -04:00
" key in missing snapshot: %s " ,
2022-02-25 13:18:19 -05:00
( bch2_bkey_val_to_text ( & buf , c , k ) , buf . buf ) ) )
ret = bch2_btree_delete_at ( trans , iter ,
2021-10-28 16:16:55 -04:00
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE ) ? : 1 ;
2021-04-20 00:15:44 -04:00
fsck_err :
2022-02-25 13:18:19 -05:00
printbuf_exit ( & buf ) ;
2021-04-20 00:15:44 -04:00
return ret ;
2021-07-14 20:28:27 -04:00
}
2021-04-07 01:55:57 -04:00
static int hash_redo_key ( struct btree_trans * trans ,
const struct bch_hash_desc desc ,
struct bch_hash_info * hash_info ,
struct btree_iter * k_iter , struct bkey_s_c k )
2017-03-16 22:18:50 -08:00
{
2021-04-20 00:15:44 -04:00
bch_err ( trans - > c , " hash_redo_key() not implemented yet " ) ;
return - EINVAL ;
#if 0
2021-04-24 22:33:25 -04:00
struct bkey_i * delete ;
2017-03-16 22:18:50 -08:00
struct bkey_i * tmp ;
2021-04-24 22:33:25 -04:00
delete = bch2_trans_kmalloc ( trans , sizeof ( * delete ) ) ;
if ( IS_ERR ( delete ) )
return PTR_ERR ( delete ) ;
2019-12-22 23:04:30 -05:00
tmp = bch2_trans_kmalloc ( trans , bkey_bytes ( k . k ) ) ;
if ( IS_ERR ( tmp ) )
return PTR_ERR ( tmp ) ;
2017-03-16 22:18:50 -08:00
bkey_reassemble ( tmp , k ) ;
2021-04-24 22:33:25 -04:00
bkey_init ( & delete - > k ) ;
delete - > k . p = k_iter - > pos ;
2021-06-14 18:16:10 -04:00
return bch2_btree_iter_traverse ( k_iter ) ? :
bch2_trans_update ( trans , k_iter , delete , 0 ) ? :
2021-06-02 00:15:07 -04:00
bch2_hash_set ( trans , desc , hash_info , k_iter - > pos . inode , tmp , 0 ) ;
2021-04-20 00:15:44 -04:00
# endif
2017-03-16 22:18:50 -08:00
}
2021-04-07 01:55:57 -04:00
static int hash_check_key ( struct btree_trans * trans ,
const struct bch_hash_desc desc ,
struct bch_hash_info * hash_info ,
struct btree_iter * k_iter , struct bkey_s_c hash_k )
2018-07-12 19:19:41 -04:00
{
2019-03-25 15:10:15 -04:00
struct bch_fs * c = trans - > c ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter = { NULL } ;
2022-02-25 13:18:19 -05:00
struct printbuf buf = PRINTBUF ;
2021-04-07 01:55:57 -04:00
struct bkey_s_c k ;
u64 hash ;
2018-07-12 19:19:41 -04:00
int ret = 0 ;
2021-04-07 01:55:57 -04:00
if ( hash_k . k - > type ! = desc . key_type )
return 0 ;
hash = desc . hash_bkey ( hash_info , hash_k ) ;
if ( likely ( hash = = hash_k . k - > p . offset ) )
2018-07-12 19:19:41 -04:00
return 0 ;
2021-04-07 01:55:57 -04:00
if ( hash_k . k - > p . offset < hash )
goto bad_hash ;
2018-07-12 19:19:41 -04:00
2022-04-17 19:02:04 -04:00
for_each_btree_key_norestart ( trans , iter , desc . btree_id ,
POS ( hash_k . k - > p . inode , hash ) ,
BTREE_ITER_SLOTS , k , ret ) {
2021-04-07 01:55:57 -04:00
if ( ! bkey_cmp ( k . k - > p , hash_k . k - > p ) )
2018-07-12 19:19:41 -04:00
break ;
2021-04-07 01:55:57 -04:00
if ( fsck_err_on ( k . k - > type = = desc . key_type & &
! desc . cmp_bkey ( k , hash_k ) , c ,
2018-07-12 19:19:41 -04:00
" duplicate hash table keys: \n %s " ,
2022-02-25 13:18:19 -05:00
( printbuf_reset ( & buf ) ,
bch2_bkey_val_to_text ( & buf , c , hash_k ) ,
buf . buf ) ) ) {
2021-10-28 16:16:55 -04:00
ret = bch2_hash_delete_at ( trans , desc , hash_info , k_iter , 0 ) ? : 1 ;
2018-07-12 19:19:41 -04:00
break ;
}
2021-04-07 01:55:57 -04:00
if ( bkey_deleted ( k . k ) ) {
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( trans , & iter ) ;
2021-04-07 01:55:57 -04:00
goto bad_hash ;
2017-03-16 22:18:50 -08:00
}
2021-04-07 01:55:57 -04:00
}
2022-02-25 13:18:19 -05:00
out :
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( trans , & iter ) ;
2022-02-25 13:18:19 -05:00
printbuf_exit ( & buf ) ;
2017-03-16 22:18:50 -08:00
return ret ;
2021-04-07 01:55:57 -04:00
bad_hash :
2022-04-17 19:02:04 -04:00
if ( fsck_err ( c , " hash table key at wrong offset: btree %s inode %llu offset %llu, "
2021-04-24 22:33:25 -04:00
" hashed to %llu \n %s " ,
2022-04-17 19:02:04 -04:00
bch2_btree_ids [ desc . btree_id ] , hash_k . k - > p . inode , hash_k . k - > p . offset , hash ,
2022-02-25 13:18:19 -05:00
( printbuf_reset ( & buf ) ,
bch2_bkey_val_to_text ( & buf , c , hash_k ) , buf . buf ) ) = = FSCK_ERR_IGNORE )
2018-08-21 19:42:00 -04:00
return 0 ;
2021-10-28 16:16:55 -04:00
ret = hash_redo_key ( trans , desc , hash_info , k_iter , hash_k ) ;
2021-04-07 01:55:57 -04:00
if ( ret ) {
bch_err ( c , " hash_redo_key err %i " , ret ) ;
return ret ;
2018-08-21 19:42:00 -04:00
}
2022-02-25 13:18:19 -05:00
ret = - EINTR ;
2018-08-21 19:42:00 -04:00
fsck_err :
2022-02-25 13:18:19 -05:00
goto out ;
2018-08-21 19:42:00 -04:00
}
2021-04-06 21:41:48 -04:00
static int check_inode ( struct btree_trans * trans ,
struct btree_iter * iter ,
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ,
2021-04-20 00:15:44 -04:00
struct bch_inode_unpacked * prev ,
2022-07-14 02:47:36 -04:00
struct snapshots_seen * s ,
2021-10-28 16:16:55 -04:00
bool full )
2021-04-06 21:41:48 -04:00
{
struct bch_fs * c = trans - > c ;
2021-10-28 16:16:55 -04:00
struct bch_inode_unpacked u ;
2021-04-06 21:41:48 -04:00
bool do_update = false ;
2021-10-28 16:16:55 -04:00
int ret ;
ret = check_key_has_snapshot ( trans , iter , k ) ;
2022-04-11 22:36:53 -04:00
if ( ret < 0 )
goto err ;
2021-10-28 16:16:55 -04:00
if ( ret )
2022-04-11 22:36:53 -04:00
return 0 ;
2021-10-28 16:16:55 -04:00
2022-07-14 02:47:36 -04:00
ret = snapshots_seen_update ( c , s , iter - > btree_id , k . k - > p ) ;
if ( ret )
goto err ;
2021-10-28 16:16:55 -04:00
/*
* if snapshot id isn ' t a leaf node , skip it - deletion in
* particular is not atomic , so on the internal snapshot nodes
* we can see inodes marked for deletion after a clean shutdown
*/
if ( bch2_snapshot_internal_node ( c , k . k - > p . snapshot ) )
return 0 ;
2021-10-29 21:14:23 -04:00
if ( ! bkey_is_inode ( k . k ) )
2021-10-28 16:16:55 -04:00
return 0 ;
2021-10-29 21:14:23 -04:00
BUG_ON ( bch2_inode_unpack ( k , & u ) ) ;
2021-10-28 16:16:55 -04:00
if ( ! full & &
2021-10-29 21:14:23 -04:00
! ( u . bi_flags & ( BCH_INODE_I_SIZE_DIRTY |
BCH_INODE_I_SECTORS_DIRTY |
BCH_INODE_UNLINKED ) ) )
2021-10-28 16:16:55 -04:00
return 0 ;
if ( prev - > bi_inum ! = u . bi_inum )
* prev = u ;
if ( fsck_err_on ( prev - > bi_hash_seed ! = u . bi_hash_seed | |
inode_d_type ( prev ) ! = inode_d_type ( & u ) , c ,
2021-04-20 00:15:44 -04:00
" inodes in different snapshots don't match " ) ) {
bch_err ( c , " repair not implemented yet " ) ;
return - EINVAL ;
}
2021-04-06 21:41:48 -04:00
if ( u . bi_flags & BCH_INODE_UNLINKED & &
( ! c - > sb . clean | |
fsck_err ( c , " filesystem marked clean, but inode %llu unlinked " ,
u . bi_inum ) ) ) {
bch2_trans_unlock ( trans ) ;
bch2_fs_lazy_rw ( c ) ;
2021-04-20 00:15:44 -04:00
ret = fsck_inode_rm ( trans , u . bi_inum , iter - > pos . snapshot ) ;
2021-04-06 21:41:48 -04:00
if ( ret )
bch_err ( c , " error in fsck: error %i while deleting inode " , ret ) ;
return ret ;
}
if ( u . bi_flags & BCH_INODE_I_SIZE_DIRTY & &
( ! c - > sb . clean | |
fsck_err ( c , " filesystem marked clean, but inode %llu has i_size dirty " ,
u . bi_inum ) ) ) {
bch_verbose ( c , " truncating inode %llu " , u . bi_inum ) ;
bch2_trans_unlock ( trans ) ;
bch2_fs_lazy_rw ( c ) ;
/*
* XXX : need to truncate partial blocks too here - or ideally
* just switch units to bytes and that issue goes away
*/
ret = bch2_btree_delete_range_trans ( trans , BTREE_ID_extents ,
2021-04-20 00:15:44 -04:00
SPOS ( u . bi_inum , round_up ( u . bi_size , block_bytes ( c ) ) > > 9 ,
iter - > pos . snapshot ) ,
2021-04-06 21:41:48 -04:00
POS ( u . bi_inum , U64_MAX ) ,
2021-04-20 00:15:44 -04:00
0 , NULL ) ;
2021-04-06 21:41:48 -04:00
if ( ret ) {
bch_err ( c , " error in fsck: error %i truncating inode " , ret ) ;
return ret ;
}
/*
* We truncated without our normal sector accounting hook , just
* make sure we recalculate it :
*/
u . bi_flags | = BCH_INODE_I_SECTORS_DIRTY ;
u . bi_flags & = ~ BCH_INODE_I_SIZE_DIRTY ;
do_update = true ;
}
if ( u . bi_flags & BCH_INODE_I_SECTORS_DIRTY & &
( ! c - > sb . clean | |
fsck_err ( c , " filesystem marked clean, but inode %llu has i_sectors dirty " ,
u . bi_inum ) ) ) {
s64 sectors ;
bch_verbose ( c , " recounting sectors for inode %llu " ,
u . bi_inum ) ;
2021-04-20 00:15:44 -04:00
sectors = bch2_count_inode_sectors ( trans , u . bi_inum , iter - > pos . snapshot ) ;
2021-04-06 21:41:48 -04:00
if ( sectors < 0 ) {
bch_err ( c , " error in fsck: error %i recounting inode sectors " ,
( int ) sectors ) ;
return sectors ;
}
u . bi_sectors = sectors ;
u . bi_flags & = ~ BCH_INODE_I_SECTORS_DIRTY ;
do_update = true ;
}
2021-04-07 03:11:07 -04:00
if ( u . bi_flags & BCH_INODE_BACKPTR_UNTRUSTED ) {
u . bi_dir = 0 ;
u . bi_dir_offset = 0 ;
u . bi_flags & = ~ BCH_INODE_BACKPTR_UNTRUSTED ;
2021-04-06 21:41:48 -04:00
do_update = true ;
}
if ( do_update ) {
2022-07-15 20:51:09 -04:00
ret = __write_inode ( trans , & u , iter - > pos . snapshot ) ;
2021-04-06 21:41:48 -04:00
if ( ret )
bch_err ( c , " error in fsck: error %i "
" updating inode " , ret ) ;
}
2022-04-11 22:36:53 -04:00
err :
2021-04-06 21:41:48 -04:00
fsck_err :
2022-04-11 22:36:53 -04:00
if ( ret )
bch_err ( c , " error %i from check_inode() " , ret ) ;
2021-04-06 21:41:48 -04:00
return ret ;
}
noinline_for_stack
static int check_inodes ( struct bch_fs * c , bool full )
{
struct btree_trans trans ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2021-10-28 16:16:55 -04:00
struct bch_inode_unpacked prev = { 0 } ;
2022-07-14 02:47:36 -04:00
struct snapshots_seen s ;
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ;
2021-04-06 21:41:48 -04:00
int ret ;
2022-07-14 02:47:36 -04:00
snapshots_seen_init ( & s ) ;
2021-04-06 21:41:48 -04:00
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
2022-07-15 20:51:09 -04:00
ret = for_each_btree_key_commit ( & trans , iter , BTREE_ID_inodes ,
POS_MIN ,
BTREE_ITER_PREFETCH | BTREE_ITER_ALL_SNAPSHOTS ,
k ,
NULL , NULL ,
BTREE_INSERT_LAZY_RW | BTREE_INSERT_NOFAIL ,
check_inode ( & trans , & iter , k , & prev , & s , full ) ) ;
2021-04-20 00:15:44 -04:00
2021-10-28 16:16:55 -04:00
bch2_trans_exit ( & trans ) ;
2022-07-14 02:47:36 -04:00
snapshots_seen_exit ( & s ) ;
2022-04-11 22:36:53 -04:00
if ( ret )
bch_err ( c , " error %i from check_inodes() " , ret ) ;
2021-10-28 16:16:55 -04:00
return ret ;
}
2021-04-20 00:15:44 -04:00
/*
* Checking for overlapping extents needs to be reimplemented
*/
#if 0
2021-03-19 22:34:54 -04:00
static int fix_overlapping_extent ( struct btree_trans * trans ,
2019-12-30 14:37:25 -05:00
struct bkey_s_c k , struct bpos cut_at )
{
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2019-12-30 14:37:25 -05:00
struct bkey_i * u ;
int ret ;
u = bch2_trans_kmalloc ( trans , bkey_bytes ( k . k ) ) ;
ret = PTR_ERR_OR_ZERO ( u ) ;
if ( ret )
return ret ;
bkey_reassemble ( u , k ) ;
bch2_cut_front ( cut_at , u ) ;
2021-04-20 00:15:44 -04:00
/*
* We don ' t want to go through the extent_handle_overwrites path :
*
* XXX : this is going to screw up disk accounting , extent triggers
* assume things about extent overwrites - we should be running the
* triggers manually here
*/
bch2_trans_iter_init ( trans , & iter , BTREE_ID_extents , u - > k . p ,
BTREE_ITER_INTENT | BTREE_ITER_NOT_EXTENTS ) ;
BUG_ON ( iter . flags & BTREE_ITER_IS_EXTENTS ) ;
ret = bch2_btree_iter_traverse ( & iter ) ? :
bch2_trans_update ( trans , & iter , u , BTREE_TRIGGER_NORUN ) ? :
bch2_trans_commit ( trans , NULL , NULL ,
BTREE_INSERT_NOFAIL |
BTREE_INSERT_LAZY_RW ) ;
bch2_trans_iter_exit ( trans , & iter ) ;
return ret ;
}
# endif
2021-10-12 12:06:02 -04:00
static struct bkey_s_c_dirent dirent_get_by_pos ( struct btree_trans * trans ,
struct btree_iter * iter ,
struct bpos pos )
{
struct bkey_s_c k ;
int ret ;
bch2_trans_iter_init ( trans , iter , BTREE_ID_dirents , pos , 0 ) ;
k = bch2_btree_iter_peek_slot ( iter ) ;
ret = bkey_err ( k ) ;
if ( ! ret & & k . k - > type ! = KEY_TYPE_dirent )
ret = - ENOENT ;
if ( ret ) {
bch2_trans_iter_exit ( trans , iter ) ;
return ( struct bkey_s_c_dirent ) { . k = ERR_PTR ( ret ) } ;
}
return bkey_s_c_to_dirent ( k ) ;
}
static bool inode_points_to_dirent ( struct bch_inode_unpacked * inode ,
struct bkey_s_c_dirent d )
{
return inode - > bi_dir = = d . k - > p . inode & &
inode - > bi_dir_offset = = d . k - > p . offset ;
}
static bool dirent_points_to_inode ( struct bkey_s_c_dirent d ,
struct bch_inode_unpacked * inode )
{
return d . v - > d_type = = DT_SUBVOL
? le32_to_cpu ( d . v - > d_child_subvol ) = = inode - > bi_subvol
: le64_to_cpu ( d . v - > d_inum ) = = inode - > bi_inum ;
}
2021-04-20 00:15:44 -04:00
static int inode_backpointer_exists ( struct btree_trans * trans ,
struct bch_inode_unpacked * inode ,
u32 snapshot )
{
struct btree_iter iter ;
2021-10-12 12:06:02 -04:00
struct bkey_s_c_dirent d ;
2021-04-20 00:15:44 -04:00
int ret ;
2021-10-12 12:06:02 -04:00
d = dirent_get_by_pos ( trans , & iter ,
SPOS ( inode - > bi_dir , inode - > bi_dir_offset , snapshot ) ) ;
ret = bkey_err ( d . s_c ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
2022-04-11 22:36:13 -04:00
return ret = = - ENOENT ? 0 : ret ;
2021-10-19 01:08:05 -04:00
2021-10-12 12:06:02 -04:00
ret = dirent_points_to_inode ( d , inode ) ;
2021-04-20 00:15:44 -04:00
bch2_trans_iter_exit ( trans , & iter ) ;
return ret ;
}
static int check_i_sectors ( struct btree_trans * trans , struct inode_walker * w )
{
struct bch_fs * c = trans - > c ;
struct inode_walker_entry * i ;
int ret = 0 , ret2 = 0 ;
s64 count2 ;
2022-03-29 15:48:45 -04:00
darray_for_each ( w - > inodes , i ) {
2021-04-20 00:15:44 -04:00
if ( i - > inode . bi_sectors = = i - > count )
continue ;
count2 = lockrestart_do ( trans ,
bch2_count_inode_sectors ( trans , w - > cur_inum , i - > snapshot ) ) ;
if ( i - > count ! = count2 ) {
bch_err ( c , " fsck counted i_sectors wrong: got %llu should be %llu " ,
i - > count , count2 ) ;
i - > count = count2 ;
if ( i - > inode . bi_sectors = = i - > count )
continue ;
}
if ( fsck_err_on ( ! ( i - > inode . bi_flags & BCH_INODE_I_SECTORS_DIRTY ) , c ,
" inode %llu:%u has incorrect i_sectors: got %llu, should be %llu " ,
w - > cur_inum , i - > snapshot ,
i - > inode . bi_sectors , i - > count ) = = FSCK_ERR_IGNORE )
continue ;
i - > inode . bi_sectors = i - > count ;
ret = write_inode ( trans , & i - > inode , i - > snapshot ) ;
if ( ret )
break ;
ret2 = - EINTR ;
}
fsck_err :
2022-04-11 22:36:53 -04:00
if ( ret )
bch_err ( c , " error %i from check_i_sectors() " , ret ) ;
2021-04-20 00:15:44 -04:00
return ret ? : ret2 ;
}
static int check_extent ( struct btree_trans * trans , struct btree_iter * iter ,
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ,
2021-04-20 00:15:44 -04:00
struct inode_walker * inode ,
struct snapshots_seen * s )
{
struct bch_fs * c = trans - > c ;
struct inode_walker_entry * i ;
2022-02-25 13:18:19 -05:00
struct printbuf buf = PRINTBUF ;
2022-07-14 02:47:36 -04:00
struct bpos equiv ;
2021-04-20 00:15:44 -04:00
int ret = 0 ;
ret = check_key_has_snapshot ( trans , iter , k ) ;
2022-02-25 13:18:19 -05:00
if ( ret ) {
ret = ret < 0 ? ret : 0 ;
goto out ;
}
2021-04-20 00:15:44 -04:00
2022-07-14 02:47:36 -04:00
equiv = k . k - > p ;
equiv . snapshot = bch2_snapshot_equiv ( c , k . k - > p . snapshot ) ;
ret = snapshots_seen_update ( c , s , iter - > btree_id , k . k - > p ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-20 00:15:44 -04:00
if ( k . k - > type = = KEY_TYPE_whiteout )
2022-02-25 13:18:19 -05:00
goto out ;
2021-04-20 00:15:44 -04:00
if ( inode - > cur_inum ! = k . k - > p . inode ) {
ret = check_i_sectors ( trans , inode ) ;
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-20 00:15:44 -04:00
}
2022-04-06 14:35:10 -04:00
if ( ! iter - > path - > should_be_locked ) {
/*
* hack : check_i_sectors may have handled a transaction restart ,
* it shouldn ' t be but we need to fix the new i_sectors check
* code and delete the old bch2_count_inode_sectors ( ) first
*/
2022-07-15 20:51:09 -04:00
return - EINTR ;
2022-04-06 14:35:10 -04:00
}
2021-04-20 00:15:44 -04:00
#if 0
if ( bkey_cmp ( prev . k - > k . p , bkey_start_pos ( k . k ) ) > 0 ) {
char buf1 [ 200 ] ;
char buf2 [ 200 ] ;
bch2_bkey_val_to_text ( & PBUF ( buf1 ) , c , bkey_i_to_s_c ( prev . k ) ) ;
bch2_bkey_val_to_text ( & PBUF ( buf2 ) , c , k ) ;
2022-02-25 13:18:19 -05:00
if ( fsck_err ( c , " overlapping extents: \n %s \n %s " , buf1 , buf2 ) ) {
ret = fix_overlapping_extent ( trans , k , prev . k - > k . p ) ? : - EINTR ;
goto out ;
}
2021-04-20 00:15:44 -04:00
}
# endif
2022-07-14 02:47:36 -04:00
ret = __walk_inode ( trans , inode , equiv ) ;
2021-04-20 00:15:44 -04:00
if ( ret < 0 )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-20 00:15:44 -04:00
if ( fsck_err_on ( ret = = INT_MAX , c ,
" extent in missing inode: \n %s " ,
2022-02-25 13:18:19 -05:00
( printbuf_reset ( & buf ) ,
bch2_bkey_val_to_text ( & buf , c , k ) , buf . buf ) ) ) {
ret = bch2_btree_delete_at ( trans , iter ,
2021-10-28 16:16:55 -04:00
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE ) ;
2022-02-25 13:18:19 -05:00
goto out ;
}
2021-04-20 00:15:44 -04:00
2022-02-25 13:18:19 -05:00
if ( ret = = INT_MAX ) {
ret = 0 ;
goto out ;
}
2019-12-30 14:37:25 -05:00
2022-03-29 15:48:45 -04:00
i = inode - > inodes . data + ret ;
2021-04-20 00:15:44 -04:00
ret = 0 ;
2019-12-30 14:37:25 -05:00
2021-04-20 00:15:44 -04:00
if ( fsck_err_on ( ! S_ISREG ( i - > inode . bi_mode ) & &
! S_ISLNK ( i - > inode . bi_mode ) , c ,
" extent in non regular inode mode %o: \n %s " ,
i - > inode . bi_mode ,
2022-02-25 13:18:19 -05:00
( printbuf_reset ( & buf ) ,
bch2_bkey_val_to_text ( & buf , c , k ) , buf . buf ) ) ) {
ret = bch2_btree_delete_at ( trans , iter ,
2021-10-28 16:16:55 -04:00
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE ) ;
2022-02-25 13:18:19 -05:00
goto out ;
}
2021-04-20 00:15:44 -04:00
2022-07-16 23:31:28 -04:00
/*
* Check inodes in reverse order , from oldest snapshots to newest , so
* that we emit the fewest number of whiteouts necessary :
*/
for ( i = inode - > inodes . data + inode - > inodes . nr - 1 ;
i > = inode - > inodes . data ;
- - i ) {
if ( i - > snapshot > equiv . snapshot | |
! key_visible_in_snapshot ( c , s , i - > snapshot , equiv . snapshot ) )
continue ;
if ( fsck_err_on ( ! ( i - > inode . bi_flags & BCH_INODE_I_SIZE_DIRTY ) & &
k . k - > type ! = KEY_TYPE_reservation & &
k . k - > p . offset > round_up ( i - > inode . bi_size , block_bytes ( c ) ) > > 9 , c ,
" extent type past end of inode %llu:%u, i_size %llu \n %s " ,
i - > inode . bi_inum , i - > snapshot , i - > inode . bi_size ,
( bch2_bkey_val_to_text ( & buf , c , k ) , buf . buf ) ) ) {
struct btree_iter iter2 ;
bch2_trans_copy_iter ( & iter2 , iter ) ;
bch2_btree_iter_set_snapshot ( & iter2 , i - > snapshot ) ;
ret = bch2_btree_iter_traverse ( & iter2 ) ? :
bch2_btree_delete_at ( trans , & iter2 ,
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE ) ;
bch2_trans_iter_exit ( trans , & iter2 ) ;
if ( ret )
goto err ;
if ( i - > snapshot ! = equiv . snapshot ) {
ret = snapshots_seen_add ( c , s , i - > snapshot ) ;
if ( ret )
goto err ;
2021-04-20 00:15:44 -04:00
}
}
}
2021-04-06 20:15:26 -04:00
2021-04-20 00:15:44 -04:00
if ( bkey_extent_is_allocation ( k . k ) )
2022-07-14 02:47:36 -04:00
for_each_visible_inode ( c , s , inode , equiv . snapshot , i )
2021-04-20 00:15:44 -04:00
i - > count + = k . k - > size ;
#if 0
bch2_bkey_buf_reassemble ( & prev , c , k ) ;
# endif
2021-04-06 20:15:26 -04:00
2022-02-25 13:18:19 -05:00
out :
err :
2021-04-20 00:15:44 -04:00
fsck_err :
2022-02-25 13:18:19 -05:00
printbuf_exit ( & buf ) ;
2022-04-11 22:36:53 -04:00
if ( ret & & ret ! = - EINTR )
bch_err ( c , " error %i from check_extent() " , ret ) ;
2021-04-06 20:15:26 -04:00
return ret ;
}
2017-03-16 22:18:50 -08:00
/*
* Walk extents : verify that extents have a corresponding S_ISREG inode , and
* that i_size an i_sectors are consistent
*/
noinline_for_stack
static int check_extents ( struct bch_fs * c )
{
struct inode_walker w = inode_walker_init ( ) ;
2021-04-20 00:15:44 -04:00
struct snapshots_seen s ;
2019-03-25 15:10:15 -04:00
struct btree_trans trans ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ;
2017-03-16 22:18:50 -08:00
int ret = 0 ;
2021-04-20 00:15:44 -04:00
#if 0
struct bkey_buf prev ;
2020-12-17 15:08:58 -05:00
bch2_bkey_buf_init ( & prev ) ;
2020-03-25 16:13:00 -04:00
prev . k - > k = KEY ( 0 , 0 , 0 ) ;
2021-04-20 00:15:44 -04:00
# endif
snapshots_seen_init ( & s ) ;
2019-05-15 10:54:43 -04:00
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
2019-03-25 15:10:15 -04:00
2017-03-16 22:18:50 -08:00
bch_verbose ( c , " checking extents " ) ;
2022-07-15 20:51:09 -04:00
ret = for_each_btree_key_commit ( & trans , iter , BTREE_ID_extents ,
POS ( BCACHEFS_ROOT_INO , 0 ) ,
BTREE_ITER_PREFETCH | BTREE_ITER_ALL_SNAPSHOTS , k ,
NULL , NULL ,
BTREE_INSERT_LAZY_RW | BTREE_INSERT_NOFAIL ,
check_extent ( & trans , & iter , k , & w , & s ) ) ;
2021-04-20 00:15:44 -04:00
#if 0
bch2_bkey_buf_exit ( & prev , c ) ;
# endif
inode_walker_exit ( & w ) ;
bch2_trans_exit ( & trans ) ;
snapshots_seen_exit ( & s ) ;
2022-04-11 22:36:53 -04:00
if ( ret )
bch_err ( c , " error %i from check_extents() " , ret ) ;
2021-04-20 00:15:44 -04:00
return ret ;
}
static int check_subdir_count ( struct btree_trans * trans , struct inode_walker * w )
{
struct bch_fs * c = trans - > c ;
struct inode_walker_entry * i ;
int ret = 0 , ret2 = 0 ;
s64 count2 ;
2022-03-29 15:48:45 -04:00
darray_for_each ( w - > inodes , i ) {
2021-04-20 00:15:44 -04:00
if ( i - > inode . bi_nlink = = i - > count )
continue ;
2022-02-13 20:42:12 -05:00
count2 = bch2_count_subdirs ( trans , w - > cur_inum , i - > snapshot ) ;
if ( count2 < 0 )
return count2 ;
2021-04-20 00:15:44 -04:00
if ( i - > count ! = count2 ) {
bch_err ( c , " fsck counted subdirectories wrong: got %llu should be %llu " ,
i - > count , count2 ) ;
i - > count = count2 ;
if ( i - > inode . bi_nlink = = i - > count )
continue ;
}
if ( fsck_err_on ( i - > inode . bi_nlink ! = i - > count , c ,
" directory %llu:%u with wrong i_nlink: got %u, should be %llu " ,
w - > cur_inum , i - > snapshot , i - > inode . bi_nlink , i - > count ) ) {
i - > inode . bi_nlink = i - > count ;
ret = write_inode ( trans , & i - > inode , i - > snapshot ) ;
2021-03-19 22:34:54 -04:00
if ( ret )
break ;
2021-04-20 00:15:44 -04:00
ret2 = - EINTR ;
2021-03-19 22:34:54 -04:00
}
2021-04-20 00:15:44 -04:00
}
fsck_err :
2022-04-11 22:36:53 -04:00
if ( ret )
bch_err ( c , " error %i from check_subdir_count() " , ret ) ;
2021-04-20 00:15:44 -04:00
return ret ? : ret2 ;
}
2021-03-19 22:34:54 -04:00
2021-04-20 00:15:44 -04:00
static int check_dirent_target ( struct btree_trans * trans ,
struct btree_iter * iter ,
struct bkey_s_c_dirent d ,
struct bch_inode_unpacked * target ,
u32 target_snapshot )
{
struct bch_fs * c = trans - > c ;
2021-10-28 16:16:55 -04:00
struct bkey_i_dirent * n ;
2021-04-20 00:15:44 -04:00
bool backpointer_exists = true ;
2022-02-25 13:18:19 -05:00
struct printbuf buf = PRINTBUF ;
2021-04-20 00:15:44 -04:00
int ret = 0 ;
if ( ! target - > bi_dir & &
! target - > bi_dir_offset ) {
target - > bi_dir = d . k - > p . inode ;
target - > bi_dir_offset = d . k - > p . offset ;
2021-10-28 16:16:55 -04:00
ret = __write_inode ( trans , target , target_snapshot ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
goto err ;
}
2021-10-12 12:06:02 -04:00
if ( ! inode_points_to_dirent ( target , d ) ) {
2021-04-20 00:15:44 -04:00
ret = inode_backpointer_exists ( trans , target , d . k - > p . snapshot ) ;
if ( ret < 0 )
goto err ;
2019-12-30 14:37:25 -05:00
2021-04-20 00:15:44 -04:00
backpointer_exists = ret ;
ret = 0 ;
2019-12-30 14:37:25 -05:00
2021-04-20 00:15:44 -04:00
if ( fsck_err_on ( S_ISDIR ( target - > bi_mode ) & &
backpointer_exists , c ,
" directory %llu with multiple links " ,
target - > bi_inum ) ) {
2021-10-28 16:16:55 -04:00
ret = __remove_dirent ( trans , d . k - > p ) ;
2022-02-25 13:18:19 -05:00
goto out ;
2019-12-30 14:37:25 -05:00
}
2021-04-20 00:15:44 -04:00
if ( fsck_err_on ( backpointer_exists & &
! target - > bi_nlink , c ,
2022-04-16 18:59:58 -04:00
" inode %llu type %s has multiple links but i_nlink 0 " ,
target - > bi_inum , bch2_d_types [ d . v - > d_type ] ) ) {
2021-04-20 00:15:44 -04:00
target - > bi_nlink + + ;
target - > bi_flags & = ~ BCH_INODE_UNLINKED ;
2017-03-16 22:18:50 -08:00
2021-10-28 16:16:55 -04:00
ret = __write_inode ( trans , target , target_snapshot ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
goto err ;
2017-03-16 22:18:50 -08:00
}
2021-04-20 00:15:44 -04:00
if ( fsck_err_on ( ! backpointer_exists , c ,
2021-10-20 17:59:38 -04:00
" inode %llu:%u has wrong backpointer: \n "
2021-04-20 00:15:44 -04:00
" got %llu:%llu \n "
" should be %llu:%llu " ,
2021-10-20 17:59:38 -04:00
target - > bi_inum , target_snapshot ,
2021-04-20 00:15:44 -04:00
target - > bi_dir ,
target - > bi_dir_offset ,
d . k - > p . inode ,
d . k - > p . offset ) ) {
target - > bi_dir = d . k - > p . inode ;
target - > bi_dir_offset = d . k - > p . offset ;
2021-10-28 16:16:55 -04:00
ret = __write_inode ( trans , target , target_snapshot ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
goto err ;
2017-03-16 22:18:50 -08:00
}
2021-04-20 00:15:44 -04:00
}
2017-03-16 22:18:50 -08:00
2021-10-28 16:16:55 -04:00
if ( fsck_err_on ( d . v - > d_type ! = inode_d_type ( target ) , c ,
" incorrect d_type: got %s, should be %s: \n %s " ,
bch2_d_type_str ( d . v - > d_type ) ,
bch2_d_type_str ( inode_d_type ( target ) ) ,
2022-02-25 13:18:19 -05:00
( printbuf_reset ( & buf ) ,
bch2_bkey_val_to_text ( & buf , c , d . s_c ) , buf . buf ) ) ) {
2021-10-28 16:16:55 -04:00
n = bch2_trans_kmalloc ( trans , bkey_bytes ( d . k ) ) ;
ret = PTR_ERR_OR_ZERO ( n ) ;
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-20 00:15:44 -04:00
bkey_reassemble ( & n - > k_i , d . s_c ) ;
2021-10-28 16:16:55 -04:00
n - > v . d_type = inode_d_type ( target ) ;
2021-04-20 00:15:44 -04:00
2021-10-28 16:16:55 -04:00
ret = bch2_trans_update ( trans , iter , & n - > k_i , 0 ) ;
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-10-12 12:06:02 -04:00
2021-10-28 16:16:55 -04:00
d = dirent_i_to_s_c ( n ) ;
2021-10-12 12:06:02 -04:00
}
if ( d . v - > d_type = = DT_SUBVOL & &
target - > bi_parent_subvol ! = le32_to_cpu ( d . v - > d_parent_subvol ) & &
( c - > sb . version < bcachefs_metadata_version_subvol_dirent | |
fsck_err ( c , " dirent has wrong d_parent_subvol field: got %u, should be %u " ,
le32_to_cpu ( d . v - > d_parent_subvol ) ,
target - > bi_parent_subvol ) ) ) {
2021-10-28 16:16:55 -04:00
n = bch2_trans_kmalloc ( trans , bkey_bytes ( d . k ) ) ;
ret = PTR_ERR_OR_ZERO ( n ) ;
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-10-12 12:06:02 -04:00
bkey_reassemble ( & n - > k_i , d . s_c ) ;
n - > v . d_parent_subvol = cpu_to_le32 ( target - > bi_parent_subvol ) ;
2021-10-28 16:16:55 -04:00
ret = bch2_trans_update ( trans , iter , & n - > k_i , 0 ) ;
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-10-12 12:06:02 -04:00
2021-10-28 16:16:55 -04:00
d = dirent_i_to_s_c ( n ) ;
2017-03-16 22:18:50 -08:00
}
2022-02-25 13:18:19 -05:00
out :
2021-04-20 00:15:44 -04:00
err :
2017-03-16 22:18:50 -08:00
fsck_err :
2022-02-25 13:18:19 -05:00
printbuf_exit ( & buf ) ;
2022-04-11 22:36:53 -04:00
if ( ret & & ret ! = - EINTR )
bch_err ( c , " error %i from check_target() " , ret ) ;
2021-04-20 00:15:44 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
}
2021-07-14 20:28:27 -04:00
static int check_dirent ( struct btree_trans * trans , struct btree_iter * iter ,
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ,
2021-07-14 20:28:27 -04:00
struct bch_hash_info * hash_info ,
2021-04-20 00:15:44 -04:00
struct inode_walker * dir ,
struct inode_walker * target ,
struct snapshots_seen * s )
2017-03-16 22:18:50 -08:00
{
2021-07-14 20:28:27 -04:00
struct bch_fs * c = trans - > c ;
struct bkey_s_c_dirent d ;
2021-04-20 00:15:44 -04:00
struct inode_walker_entry * i ;
2022-02-25 13:18:19 -05:00
struct printbuf buf = PRINTBUF ;
2022-07-14 02:47:36 -04:00
struct bpos equiv ;
2022-02-25 13:18:19 -05:00
int ret = 0 ;
2018-07-12 19:19:41 -04:00
2021-04-20 00:15:44 -04:00
ret = check_key_has_snapshot ( trans , iter , k ) ;
2022-02-25 13:18:19 -05:00
if ( ret ) {
ret = ret < 0 ? ret : 0 ;
goto out ;
}
2017-03-16 22:18:50 -08:00
2022-07-14 02:47:36 -04:00
equiv = k . k - > p ;
equiv . snapshot = bch2_snapshot_equiv ( c , k . k - > p . snapshot ) ;
ret = snapshots_seen_update ( c , s , iter - > btree_id , k . k - > p ) ;
2021-07-14 20:28:27 -04:00
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-06 20:15:26 -04:00
2021-04-20 00:15:44 -04:00
if ( k . k - > type = = KEY_TYPE_whiteout )
2022-02-25 13:18:19 -05:00
goto out ;
2021-04-20 00:15:44 -04:00
if ( dir - > cur_inum ! = k . k - > p . inode ) {
ret = check_subdir_count ( trans , dir ) ;
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-20 00:15:44 -04:00
}
2022-04-06 14:35:10 -04:00
if ( ! iter - > path - > should_be_locked ) {
/* hack: see check_extent() */
2022-07-15 20:51:09 -04:00
return - EINTR ;
2022-04-06 14:35:10 -04:00
}
2022-07-14 02:47:36 -04:00
ret = __walk_inode ( trans , dir , equiv ) ;
2021-04-20 00:15:44 -04:00
if ( ret < 0 )
2022-02-25 13:18:19 -05:00
goto err ;
2017-03-16 22:18:50 -08:00
2021-04-20 00:15:44 -04:00
if ( fsck_err_on ( ret = = INT_MAX , c ,
2021-07-14 20:28:27 -04:00
" dirent in nonexisting directory: \n %s " ,
2022-02-25 13:18:19 -05:00
( printbuf_reset ( & buf ) ,
bch2_bkey_val_to_text ( & buf , c , k ) , buf . buf ) ) ) {
ret = bch2_btree_delete_at ( trans , iter ,
2021-10-28 16:16:55 -04:00
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE ) ;
2022-02-25 13:18:19 -05:00
goto out ;
}
2021-04-20 00:15:44 -04:00
2022-02-25 13:18:19 -05:00
if ( ret = = INT_MAX ) {
ret = 0 ;
goto out ;
}
2021-04-20 00:15:44 -04:00
2022-03-29 15:48:45 -04:00
i = dir - > inodes . data + ret ;
2021-04-20 00:15:44 -04:00
ret = 0 ;
if ( fsck_err_on ( ! S_ISDIR ( i - > inode . bi_mode ) , c ,
2021-10-28 16:16:55 -04:00
" dirent in non directory inode type %s: \n %s " ,
bch2_d_type_str ( inode_d_type ( & i - > inode ) ) ,
2022-02-25 13:18:19 -05:00
( printbuf_reset ( & buf ) ,
bch2_bkey_val_to_text ( & buf , c , k ) , buf . buf ) ) ) {
ret = bch2_btree_delete_at ( trans , iter , 0 ) ;
goto out ;
}
2021-04-06 20:15:26 -04:00
2021-04-20 00:15:44 -04:00
if ( dir - > first_this_inode )
2022-03-29 15:48:45 -04:00
* hash_info = bch2_hash_info_init ( c , & dir - > inodes . data [ 0 ] . inode ) ;
2017-03-16 22:18:50 -08:00
2021-07-14 20:28:27 -04:00
ret = hash_check_key ( trans , bch2_dirent_hash_desc ,
hash_info , iter , k ) ;
if ( ret < 0 )
2022-02-25 13:18:19 -05:00
goto err ;
if ( ret ) {
/* dirent has been deleted */
ret = 0 ;
goto out ;
}
2021-04-07 01:55:57 -04:00
2021-07-14 20:28:27 -04:00
if ( k . k - > type ! = KEY_TYPE_dirent )
2022-02-25 13:18:19 -05:00
goto out ;
2017-03-16 22:18:50 -08:00
2021-07-14 20:28:27 -04:00
d = bkey_s_c_to_dirent ( k ) ;
2017-03-16 22:18:50 -08:00
2021-10-12 12:06:02 -04:00
if ( d . v - > d_type = = DT_SUBVOL ) {
struct bch_inode_unpacked subvol_root ;
u32 target_subvol = le32_to_cpu ( d . v - > d_child_subvol ) ;
u32 target_snapshot ;
u64 target_inum ;
2021-03-16 00:46:26 -04:00
2021-10-12 12:06:02 -04:00
ret = __subvol_lookup ( trans , target_subvol ,
& target_snapshot , & target_inum ) ;
if ( ret & & ret ! = - ENOENT )
2022-02-25 13:18:19 -05:00
goto err ;
2017-03-16 22:18:50 -08:00
2021-10-12 12:06:02 -04:00
if ( fsck_err_on ( ret , c ,
" dirent points to missing subvolume %llu " ,
2022-02-25 13:18:19 -05:00
le64_to_cpu ( d . v - > d_child_subvol ) ) ) {
ret = __remove_dirent ( trans , d . k - > p ) ;
goto err ;
}
2021-07-14 20:28:27 -04:00
2021-04-20 00:15:44 -04:00
ret = __lookup_inode ( trans , target_inum ,
& subvol_root , & target_snapshot ) ;
if ( ret & & ret ! = - ENOENT )
2022-02-25 13:18:19 -05:00
goto err ;
2021-07-14 20:28:27 -04:00
2021-04-20 00:15:44 -04:00
if ( fsck_err_on ( ret , c ,
" subvolume %u points to missing subvolume root %llu " ,
target_subvol ,
target_inum ) ) {
bch_err ( c , " repair not implemented yet " ) ;
2022-02-25 13:18:19 -05:00
ret = - EINVAL ;
goto err ;
2021-04-20 00:15:44 -04:00
}
2021-07-14 20:28:27 -04:00
2021-04-20 00:15:44 -04:00
if ( fsck_err_on ( subvol_root . bi_subvol ! = target_subvol , c ,
" subvol root %llu has wrong bi_subvol field: got %u, should be %u " ,
target_inum ,
subvol_root . bi_subvol , target_subvol ) ) {
subvol_root . bi_subvol = target_subvol ;
2021-10-28 16:16:55 -04:00
ret = __write_inode ( trans , & subvol_root , target_snapshot ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-20 00:15:44 -04:00
}
2021-07-14 20:28:27 -04:00
2021-04-20 00:15:44 -04:00
ret = check_dirent_target ( trans , iter , d , & subvol_root ,
target_snapshot ) ;
2021-07-14 20:28:27 -04:00
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-20 00:15:44 -04:00
} else {
2021-10-12 12:06:02 -04:00
ret = __get_visible_inodes ( trans , target , s , le64_to_cpu ( d . v - > d_inum ) ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2017-03-16 22:18:50 -08:00
2022-03-29 15:48:45 -04:00
if ( fsck_err_on ( ! target - > inodes . nr , c ,
2022-07-14 02:47:36 -04:00
" dirent points to missing inode: (equiv %u) \n %s " ,
equiv . snapshot ,
2022-02-25 13:18:19 -05:00
( printbuf_reset ( & buf ) ,
bch2_bkey_val_to_text ( & buf , c , k ) ,
buf . buf ) ) ) {
2021-10-28 16:16:55 -04:00
ret = __remove_dirent ( trans , d . k - > p ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2017-03-16 22:18:50 -08:00
}
2022-03-29 15:48:45 -04:00
darray_for_each ( target - > inodes , i ) {
2021-04-20 00:15:44 -04:00
ret = check_dirent_target ( trans , iter , d ,
& i - > inode , i - > snapshot ) ;
if ( ret )
2022-02-25 13:18:19 -05:00
goto err ;
2021-04-07 03:11:07 -04:00
}
2021-07-14 20:28:27 -04:00
}
2021-04-07 03:11:07 -04:00
2021-04-20 00:15:44 -04:00
if ( d . v - > d_type = = DT_DIR )
2022-07-14 02:47:36 -04:00
for_each_visible_inode ( c , s , dir , equiv . snapshot , i )
2021-04-20 00:15:44 -04:00
i - > count + + ;
2017-03-16 22:18:50 -08:00
2022-02-25 13:18:19 -05:00
out :
err :
2021-07-14 20:28:27 -04:00
fsck_err :
2022-02-25 13:18:19 -05:00
printbuf_exit ( & buf ) ;
2022-04-11 22:36:53 -04:00
if ( ret & & ret ! = - EINTR )
bch_err ( c , " error %i from check_dirent() " , ret ) ;
2021-07-14 20:28:27 -04:00
return ret ;
}
2017-03-16 22:18:50 -08:00
2021-07-14 20:28:27 -04:00
/*
* Walk dirents : verify that they all have a corresponding S_ISDIR inode ,
* validate d_type
*/
noinline_for_stack
static int check_dirents ( struct bch_fs * c )
{
2021-04-20 00:15:44 -04:00
struct inode_walker dir = inode_walker_init ( ) ;
struct inode_walker target = inode_walker_init ( ) ;
struct snapshots_seen s ;
2021-07-14 20:28:27 -04:00
struct bch_hash_info hash_info ;
struct btree_trans trans ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ;
2021-07-14 20:28:27 -04:00
int ret = 0 ;
2017-03-16 22:18:50 -08:00
2021-07-14 20:28:27 -04:00
bch_verbose ( c , " checking dirents " ) ;
2021-04-20 00:15:44 -04:00
snapshots_seen_init ( & s ) ;
2021-07-14 20:28:27 -04:00
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
2017-03-16 22:18:50 -08:00
2022-07-15 20:51:09 -04:00
ret = for_each_btree_key_commit ( & trans , iter , BTREE_ID_dirents ,
POS ( BCACHEFS_ROOT_INO , 0 ) ,
BTREE_ITER_PREFETCH | BTREE_ITER_ALL_SNAPSHOTS ,
k ,
NULL , NULL ,
BTREE_INSERT_LAZY_RW | BTREE_INSERT_NOFAIL ,
check_dirent ( & trans , & iter , k , & hash_info , & dir , & target , & s ) ) ;
2021-07-14 20:28:27 -04:00
2021-04-20 00:15:44 -04:00
bch2_trans_exit ( & trans ) ;
snapshots_seen_exit ( & s ) ;
inode_walker_exit ( & dir ) ;
inode_walker_exit ( & target ) ;
2022-04-11 22:36:53 -04:00
if ( ret )
bch_err ( c , " error %i from check_dirents() " , ret ) ;
2021-04-20 00:15:44 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
}
2021-10-28 16:16:55 -04:00
static int check_xattr ( struct btree_trans * trans , struct btree_iter * iter ,
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ,
2021-10-28 16:16:55 -04:00
struct bch_hash_info * hash_info ,
struct inode_walker * inode )
{
struct bch_fs * c = trans - > c ;
int ret ;
ret = check_key_has_snapshot ( trans , iter , k ) ;
if ( ret )
return ret ;
ret = __walk_inode ( trans , inode , k . k - > p ) ;
if ( ret < 0 )
return ret ;
if ( fsck_err_on ( ret = = INT_MAX , c ,
" xattr for missing inode %llu " ,
k . k - > p . inode ) )
return bch2_btree_delete_at ( trans , iter , 0 ) ;
if ( ret = = INT_MAX )
return 0 ;
ret = 0 ;
if ( inode - > first_this_inode )
2022-03-29 15:48:45 -04:00
* hash_info = bch2_hash_info_init ( c , & inode - > inodes . data [ 0 ] . inode ) ;
2021-10-28 16:16:55 -04:00
ret = hash_check_key ( trans , bch2_xattr_hash_desc , hash_info , iter , k ) ;
fsck_err :
2022-04-11 22:36:53 -04:00
if ( ret & & ret ! = - EINTR )
bch_err ( c , " error %i from check_xattr() " , ret ) ;
2021-10-28 16:16:55 -04:00
return ret ;
}
2017-03-16 22:18:50 -08:00
/*
* Walk xattrs : verify that they all have a corresponding inode
*/
noinline_for_stack
static int check_xattrs ( struct bch_fs * c )
{
2021-10-28 16:16:55 -04:00
struct inode_walker inode = inode_walker_init ( ) ;
2021-04-07 01:55:57 -04:00
struct bch_hash_info hash_info ;
2018-07-12 19:19:41 -04:00
struct btree_trans trans ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ;
2017-03-16 22:18:50 -08:00
int ret = 0 ;
bch_verbose ( c , " checking xattrs " ) ;
2019-05-15 10:54:43 -04:00
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
2018-07-12 19:19:41 -04:00
2022-07-15 20:51:09 -04:00
ret = for_each_btree_key_commit ( & trans , iter , BTREE_ID_xattrs ,
POS ( BCACHEFS_ROOT_INO , 0 ) ,
BTREE_ITER_PREFETCH | BTREE_ITER_ALL_SNAPSHOTS ,
k ,
NULL , NULL ,
BTREE_INSERT_LAZY_RW | BTREE_INSERT_NOFAIL ,
check_xattr ( & trans , & iter , k , & hash_info , & inode ) ) ;
2021-10-28 16:16:55 -04:00
2021-10-19 15:08:00 -04:00
bch2_trans_exit ( & trans ) ;
2022-04-11 22:36:53 -04:00
if ( ret )
bch_err ( c , " error %i from check_xattrs() " , ret ) ;
2021-10-19 15:08:00 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
}
2021-10-28 16:16:55 -04:00
static int check_root_trans ( struct btree_trans * trans )
2017-03-16 22:18:50 -08:00
{
2021-10-28 16:16:55 -04:00
struct bch_fs * c = trans - > c ;
2021-04-20 00:15:44 -04:00
struct bch_inode_unpacked root_inode ;
2021-04-07 03:11:07 -04:00
u32 snapshot ;
2021-04-20 00:15:44 -04:00
u64 inum ;
2017-03-16 22:18:50 -08:00
int ret ;
2021-10-28 16:16:55 -04:00
ret = __subvol_lookup ( trans , BCACHEFS_ROOT_SUBVOL , & snapshot , & inum ) ;
2017-03-16 22:18:50 -08:00
if ( ret & & ret ! = - ENOENT )
return ret ;
2021-04-20 00:15:44 -04:00
if ( mustfix_fsck_err_on ( ret , c , " root subvol missing " ) ) {
struct bkey_i_subvolume root_subvol ;
2017-03-16 22:18:50 -08:00
2021-04-20 00:15:44 -04:00
snapshot = U32_MAX ;
inum = BCACHEFS_ROOT_INO ;
2017-03-16 22:18:50 -08:00
2021-04-20 00:15:44 -04:00
bkey_subvolume_init ( & root_subvol . k_i ) ;
root_subvol . k . p . offset = BCACHEFS_ROOT_SUBVOL ;
root_subvol . v . flags = 0 ;
root_subvol . v . snapshot = cpu_to_le32 ( snapshot ) ;
root_subvol . v . inode = cpu_to_le64 ( inum ) ;
2022-07-13 05:25:29 -04:00
ret = commit_do ( trans , NULL , NULL ,
2021-04-20 00:15:44 -04:00
BTREE_INSERT_NOFAIL |
BTREE_INSERT_LAZY_RW ,
2021-10-28 16:16:55 -04:00
__bch2_btree_insert ( trans , BTREE_ID_subvolumes , & root_subvol . k_i ) ) ;
2021-04-20 00:15:44 -04:00
if ( ret ) {
bch_err ( c , " error writing root subvol: %i " , ret ) ;
goto err ;
}
}
2021-10-28 16:16:55 -04:00
ret = __lookup_inode ( trans , BCACHEFS_ROOT_INO , & root_inode , & snapshot ) ;
2021-04-20 00:15:44 -04:00
if ( ret & & ret ! = - ENOENT )
return ret ;
2017-03-16 22:18:50 -08:00
2021-04-20 00:15:44 -04:00
if ( mustfix_fsck_err_on ( ret , c , " root directory missing " ) | |
mustfix_fsck_err_on ( ! S_ISDIR ( root_inode . bi_mode ) , c ,
" root inode not a directory " ) ) {
bch2_inode_init ( c , & root_inode , 0 , 0 , S_IFDIR | 0755 ,
0 , NULL ) ;
root_inode . bi_inum = inum ;
2017-03-16 22:18:50 -08:00
2021-10-28 16:16:55 -04:00
ret = __write_inode ( trans , & root_inode , snapshot ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
bch_err ( c , " error writing root inode: %i " , ret ) ;
}
err :
fsck_err :
return ret ;
2017-03-16 22:18:50 -08:00
}
2021-10-28 16:16:55 -04:00
/* Get root directory, create if it doesn't exist: */
noinline_for_stack
static int check_root ( struct bch_fs * c )
{
bch_verbose ( c , " checking root directory " ) ;
return bch2_trans_do ( c , NULL , NULL ,
BTREE_INSERT_NOFAIL |
BTREE_INSERT_LAZY_RW ,
check_root_trans ( & trans ) ) ;
}
2022-03-29 15:48:45 -04:00
struct pathbuf_entry {
u64 inum ;
u32 snapshot ;
2017-03-16 22:18:50 -08:00
} ;
2022-03-29 15:48:45 -04:00
typedef DARRAY ( struct pathbuf_entry ) pathbuf ;
static bool path_is_dup ( pathbuf * p , u64 inum , u32 snapshot )
2021-10-20 17:59:38 -04:00
{
struct pathbuf_entry * i ;
2022-03-29 15:48:45 -04:00
darray_for_each ( * p , i )
2021-10-20 17:59:38 -04:00
if ( i - > inum = = inum & &
i - > snapshot = = snapshot )
return true ;
return false ;
}
2022-03-29 15:48:45 -04:00
static int path_down ( struct bch_fs * c , pathbuf * p ,
2021-12-30 20:14:52 -05:00
u64 inum , u32 snapshot )
2017-03-16 22:18:50 -08:00
{
2022-03-29 15:48:45 -04:00
int ret = darray_push ( p , ( ( struct pathbuf_entry ) {
2021-10-20 17:59:38 -04:00
. inum = inum ,
. snapshot = snapshot ,
2022-03-29 15:48:45 -04:00
} ) ) ;
if ( ret )
bch_err ( c , " fsck: error allocating memory for pathbuf, size %zu " ,
p - > size ) ;
return ret ;
2017-03-16 22:18:50 -08:00
}
2021-10-20 17:59:38 -04:00
/*
* Check that a given inode is reachable from the root :
*
* XXX : we should also be verifying that inodes are in the right subvolumes
*/
2021-04-07 03:11:07 -04:00
static int check_path ( struct btree_trans * trans ,
2022-03-29 15:48:45 -04:00
pathbuf * p ,
2021-04-19 23:31:40 -04:00
struct bch_inode_unpacked * inode ,
u32 snapshot )
2017-03-16 22:18:50 -08:00
{
2021-04-07 03:11:07 -04:00
struct bch_fs * c = trans - > c ;
2017-03-16 22:18:50 -08:00
int ret = 0 ;
2022-07-14 02:47:36 -04:00
snapshot = bch2_snapshot_equiv ( c , snapshot ) ;
2021-04-07 03:11:07 -04:00
p - > nr = 0 ;
2019-03-25 15:10:15 -04:00
2021-10-19 01:08:05 -04:00
while ( ! ( inode - > bi_inum = = BCACHEFS_ROOT_INO & &
inode - > bi_subvol = = BCACHEFS_ROOT_SUBVOL ) ) {
2021-10-12 12:06:02 -04:00
struct btree_iter dirent_iter ;
struct bkey_s_c_dirent d ;
2021-10-20 17:59:38 -04:00
u32 parent_snapshot = snapshot ;
2021-10-12 12:06:02 -04:00
if ( inode - > bi_subvol ) {
2021-10-19 01:08:05 -04:00
u64 inum ;
ret = subvol_lookup ( trans , inode - > bi_parent_subvol ,
2021-10-20 17:59:38 -04:00
& parent_snapshot , & inum ) ;
2021-10-19 01:08:05 -04:00
if ( ret )
break ;
}
2021-04-07 03:11:07 -04:00
ret = lockrestart_do ( trans ,
2021-10-12 12:06:02 -04:00
PTR_ERR_OR_ZERO ( ( d = dirent_get_by_pos ( trans , & dirent_iter ,
SPOS ( inode - > bi_dir , inode - > bi_dir_offset ,
parent_snapshot ) ) ) . k ) ) ;
if ( ret & & ret ! = - ENOENT )
2021-04-07 03:11:07 -04:00
break ;
2017-03-16 22:18:50 -08:00
2021-10-12 12:06:02 -04:00
if ( ! ret & & ! dirent_points_to_inode ( d , inode ) ) {
bch2_trans_iter_exit ( trans , & dirent_iter ) ;
ret = - ENOENT ;
}
if ( ret = = - ENOENT ) {
2021-10-28 16:16:55 -04:00
if ( fsck_err ( c , " unreachable inode %llu:%u, type %s nlink %u backptr %llu:%llu " ,
2021-04-20 00:15:44 -04:00
inode - > bi_inum , snapshot ,
2021-10-28 16:16:55 -04:00
bch2_d_type_str ( inode_d_type ( inode ) ) ,
2021-04-07 03:11:07 -04:00
inode - > bi_nlink ,
inode - > bi_dir ,
inode - > bi_dir_offset ) )
2021-04-19 23:31:40 -04:00
ret = reattach_inode ( trans , inode , snapshot ) ;
2021-04-07 03:11:07 -04:00
break ;
}
2021-10-12 12:06:02 -04:00
bch2_trans_iter_exit ( trans , & dirent_iter ) ;
2017-03-16 22:18:50 -08:00
2021-04-07 03:11:07 -04:00
if ( ! S_ISDIR ( inode - > bi_mode ) )
break ;
2017-03-16 22:18:50 -08:00
2021-12-30 20:14:52 -05:00
ret = path_down ( c , p , inode - > bi_inum , snapshot ) ;
2021-04-07 03:11:07 -04:00
if ( ret ) {
bch_err ( c , " memory allocation failure " ) ;
return ret ;
}
2017-03-16 22:18:50 -08:00
2021-10-20 17:59:38 -04:00
snapshot = parent_snapshot ;
ret = lookup_inode ( trans , inode - > bi_dir , inode , & snapshot ) ;
if ( ret ) {
/* Should have been caught in dirents pass */
bch_err ( c , " error looking up parent directory: %i " , ret ) ;
break ;
}
if ( path_is_dup ( p , inode - > bi_inum , snapshot ) ) {
struct pathbuf_entry * i ;
2017-03-16 22:18:50 -08:00
2021-04-07 03:11:07 -04:00
/* XXX print path */
2021-10-20 17:59:38 -04:00
bch_err ( c , " directory structure loop " ) ;
2022-03-29 15:48:45 -04:00
darray_for_each ( * p , i )
2021-10-20 17:59:38 -04:00
pr_err ( " %llu:%u " , i - > inum , i - > snapshot ) ;
pr_err ( " %llu:%u " , inode - > bi_inum , snapshot ) ;
2021-04-07 03:11:07 -04:00
if ( ! fsck_err ( c , " directory structure loop " ) )
return 0 ;
2017-03-16 22:18:50 -08:00
2022-07-13 05:25:29 -04:00
ret = commit_do ( trans , NULL , NULL ,
2021-10-28 16:16:55 -04:00
BTREE_INSERT_NOFAIL |
BTREE_INSERT_LAZY_RW ,
2021-04-19 23:31:40 -04:00
remove_backpointer ( trans , inode ) ) ;
2017-03-16 22:18:50 -08:00
if ( ret ) {
2021-04-07 03:11:07 -04:00
bch_err ( c , " error removing dirent: %i " , ret ) ;
break ;
2017-03-16 22:18:50 -08:00
}
2021-04-19 23:31:40 -04:00
ret = reattach_inode ( trans , inode , snapshot ) ;
2017-03-16 22:18:50 -08:00
}
}
2021-04-07 03:11:07 -04:00
fsck_err :
if ( ret )
bch_err ( c , " %s: err %i " , __func__ , ret ) ;
return ret ;
}
2017-03-16 22:18:50 -08:00
2021-04-07 03:11:07 -04:00
/*
* Check for unreachable inodes , as well as loops in the directory structure :
* After check_dirents ( ) , if an inode backpointer doesn ' t exist that means it ' s
* unreachable :
*/
2021-10-28 16:16:55 -04:00
noinline_for_stack
2021-04-19 22:19:18 -04:00
static int check_directory_structure ( struct bch_fs * c )
2021-04-07 03:11:07 -04:00
{
struct btree_trans trans ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2021-04-07 03:11:07 -04:00
struct bkey_s_c k ;
struct bch_inode_unpacked u ;
2022-03-29 15:48:45 -04:00
pathbuf path = { 0 , } ;
2021-04-07 03:11:07 -04:00
int ret ;
2017-03-16 22:18:50 -08:00
2021-04-07 03:11:07 -04:00
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
2017-03-16 22:18:50 -08:00
2021-05-14 16:56:26 -04:00
for_each_btree_key ( & trans , iter , BTREE_ID_inodes , POS_MIN ,
BTREE_ITER_INTENT |
2021-04-20 00:15:44 -04:00
BTREE_ITER_PREFETCH |
BTREE_ITER_ALL_SNAPSHOTS , k , ret ) {
2021-10-29 21:14:23 -04:00
if ( ! bkey_is_inode ( k . k ) )
2017-03-16 22:18:50 -08:00
continue ;
2021-10-29 21:14:23 -04:00
ret = bch2_inode_unpack ( k , & u ) ;
2021-04-07 03:11:07 -04:00
if ( ret ) {
/* Should have been caught earlier in fsck: */
bch_err ( c , " error unpacking inode %llu: %i " , k . k - > p . offset , ret ) ;
break ;
2017-03-16 22:18:50 -08:00
}
2021-04-20 00:15:44 -04:00
if ( u . bi_flags & BCH_INODE_UNLINKED )
continue ;
2021-04-19 23:31:40 -04:00
ret = check_path ( & trans , & path , & u , iter . pos . snapshot ) ;
2021-04-07 03:11:07 -04:00
if ( ret )
break ;
2017-03-16 22:18:50 -08:00
}
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( & trans , & iter ) ;
2021-04-07 03:11:07 -04:00
BUG_ON ( ret = = - EINTR ) ;
2022-03-29 15:48:45 -04:00
darray_exit ( & path ) ;
2021-04-16 17:26:25 -04:00
2021-10-19 15:08:00 -04:00
bch2_trans_exit ( & trans ) ;
return ret ;
2017-03-16 22:18:50 -08:00
}
2021-04-21 21:08:49 -04:00
struct nlink_table {
size_t nr ;
size_t size ;
2017-03-16 22:18:50 -08:00
2021-04-21 21:08:49 -04:00
struct nlink {
u64 inum ;
u32 snapshot ;
u32 count ;
} * d ;
} ;
2017-03-16 22:18:50 -08:00
2021-12-30 20:14:52 -05:00
static int add_nlink ( struct bch_fs * c , struct nlink_table * t ,
u64 inum , u32 snapshot )
2017-03-16 22:18:50 -08:00
{
2021-04-21 21:08:49 -04:00
if ( t - > nr = = t - > size ) {
size_t new_size = max_t ( size_t , 128UL , t - > size * 2 ) ;
void * d = kvmalloc ( new_size * sizeof ( t - > d [ 0 ] ) , GFP_KERNEL ) ;
if ( ! d ) {
2021-12-30 20:14:52 -05:00
bch_err ( c , " fsck: error allocating memory for nlink_table, size %zu " ,
new_size ) ;
2021-04-21 21:08:49 -04:00
return - ENOMEM ;
}
2017-03-16 22:18:50 -08:00
2021-05-17 16:10:06 -04:00
if ( t - > d )
memcpy ( d , t - > d , t - > size * sizeof ( t - > d [ 0 ] ) ) ;
2021-04-21 21:08:49 -04:00
kvfree ( t - > d ) ;
2017-03-16 22:18:50 -08:00
2021-04-21 21:08:49 -04:00
t - > d = d ;
t - > size = new_size ;
2021-02-12 20:53:29 -05:00
}
2021-04-21 21:08:49 -04:00
t - > d [ t - > nr + + ] = ( struct nlink ) {
. inum = inum ,
. snapshot = snapshot ,
} ;
return 0 ;
}
static int nlink_cmp ( const void * _l , const void * _r )
{
const struct nlink * l = _l ;
const struct nlink * r = _r ;
return cmp_int ( l - > inum , r - > inum ) ? : cmp_int ( l - > snapshot , r - > snapshot ) ;
}
2021-04-20 00:15:44 -04:00
static void inc_link ( struct bch_fs * c , struct snapshots_seen * s ,
struct nlink_table * links ,
u64 range_start , u64 range_end , u64 inum , u32 snapshot )
2021-04-21 21:08:49 -04:00
{
struct nlink * link , key = {
. inum = inum , . snapshot = U32_MAX ,
} ;
if ( inum < range_start | | inum > = range_end )
2017-03-16 22:18:50 -08:00
return ;
2021-04-21 21:08:49 -04:00
link = __inline_bsearch ( & key , links - > d , links - > nr ,
sizeof ( links - > d [ 0 ] ) , nlink_cmp ) ;
2021-04-20 00:15:44 -04:00
if ( ! link )
return ;
while ( link > links - > d & & link [ 0 ] . inum = = link [ - 1 ] . inum )
- - link ;
for ( ; link < links - > d + links - > nr & & link - > inum = = inum ; link + + )
if ( ref_visible ( c , s , snapshot , link - > snapshot ) ) {
link - > count + + ;
if ( link - > snapshot > = snapshot )
break ;
}
2021-04-21 21:08:49 -04:00
}
noinline_for_stack
static int check_nlinks_find_hardlinks ( struct bch_fs * c ,
struct nlink_table * t ,
u64 start , u64 * end )
{
struct btree_trans trans ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2021-04-21 21:08:49 -04:00
struct bkey_s_c k ;
struct bch_inode_unpacked u ;
int ret = 0 ;
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
for_each_btree_key ( & trans , iter , BTREE_ID_inodes ,
2021-05-14 16:56:26 -04:00
POS ( 0 , start ) ,
BTREE_ITER_INTENT |
2021-04-20 00:15:44 -04:00
BTREE_ITER_PREFETCH |
BTREE_ITER_ALL_SNAPSHOTS , k , ret ) {
2021-10-29 21:14:23 -04:00
if ( ! bkey_is_inode ( k . k ) )
2021-04-21 21:08:49 -04:00
continue ;
2021-10-29 21:14:23 -04:00
/* Should never fail, checked by bch2_inode_invalid: */
BUG_ON ( bch2_inode_unpack ( k , & u ) ) ;
2021-04-21 21:08:49 -04:00
/*
* Backpointer and directory structure checks are sufficient for
* directories , since they can ' t have hardlinks :
*/
2021-10-29 21:14:23 -04:00
if ( S_ISDIR ( le16_to_cpu ( u . bi_mode ) ) )
2021-04-21 21:08:49 -04:00
continue ;
if ( ! u . bi_nlink )
continue ;
2021-12-30 20:14:52 -05:00
ret = add_nlink ( c , t , k . k - > p . offset , k . k - > p . snapshot ) ;
2021-04-21 21:08:49 -04:00
if ( ret ) {
* end = k . k - > p . offset ;
ret = 0 ;
break ;
}
2017-03-16 22:18:50 -08:00
}
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( & trans , & iter ) ;
2021-04-21 21:08:49 -04:00
bch2_trans_exit ( & trans ) ;
if ( ret )
bch_err ( c , " error in fsck: btree error %i while walking inodes " , ret ) ;
2017-03-16 22:18:50 -08:00
2021-04-21 21:08:49 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
}
noinline_for_stack
2021-04-21 21:08:49 -04:00
static int check_nlinks_walk_dirents ( struct bch_fs * c , struct nlink_table * links ,
u64 range_start , u64 range_end )
2017-03-16 22:18:50 -08:00
{
2019-03-25 15:10:15 -04:00
struct btree_trans trans ;
2021-04-20 00:15:44 -04:00
struct snapshots_seen s ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2017-03-16 22:18:50 -08:00
struct bkey_s_c k ;
struct bkey_s_c_dirent d ;
int ret ;
2021-04-20 00:15:44 -04:00
snapshots_seen_init ( & s ) ;
2019-05-15 10:54:43 -04:00
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
2019-03-25 15:10:15 -04:00
2021-05-14 16:56:26 -04:00
for_each_btree_key ( & trans , iter , BTREE_ID_dirents , POS_MIN ,
BTREE_ITER_INTENT |
2021-04-20 00:15:44 -04:00
BTREE_ITER_PREFETCH |
BTREE_ITER_ALL_SNAPSHOTS , k , ret ) {
2022-07-14 02:47:36 -04:00
ret = snapshots_seen_update ( c , & s , iter . btree_id , k . k - > p ) ;
2021-04-20 00:15:44 -04:00
if ( ret )
break ;
2017-03-16 22:18:50 -08:00
switch ( k . k - > type ) {
2018-11-01 15:10:01 -04:00
case KEY_TYPE_dirent :
2017-03-16 22:18:50 -08:00
d = bkey_s_c_to_dirent ( k ) ;
2021-04-20 00:15:44 -04:00
if ( d . v - > d_type ! = DT_DIR & &
d . v - > d_type ! = DT_SUBVOL )
inc_link ( c , & s , links , range_start , range_end ,
le64_to_cpu ( d . v - > d_inum ) ,
2022-07-14 02:47:36 -04:00
bch2_snapshot_equiv ( c , d . k - > p . snapshot ) ) ;
2017-03-16 22:18:50 -08:00
break ;
}
}
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( & trans , & iter ) ;
2021-03-19 22:34:54 -04:00
2017-03-16 22:18:50 -08:00
if ( ret )
2019-04-17 18:21:19 -04:00
bch_err ( c , " error in fsck: btree error %i while walking dirents " , ret ) ;
2017-03-16 22:18:50 -08:00
2021-04-20 00:15:44 -04:00
bch2_trans_exit ( & trans ) ;
snapshots_seen_exit ( & s ) ;
2017-03-16 22:18:50 -08:00
return ret ;
}
noinline_for_stack
2021-04-21 21:08:49 -04:00
static int check_nlinks_update_hardlinks ( struct bch_fs * c ,
struct nlink_table * links ,
2017-03-16 22:18:50 -08:00
u64 range_start , u64 range_end )
{
2019-03-13 20:49:16 -04:00
struct btree_trans trans ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2017-03-16 22:18:50 -08:00
struct bkey_s_c k ;
2021-04-21 21:08:49 -04:00
struct bch_inode_unpacked u ;
struct nlink * link = links - > d ;
2021-04-08 15:25:29 -04:00
int ret = 0 ;
2017-03-16 22:18:50 -08:00
2019-05-15 10:54:43 -04:00
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
2019-03-13 20:49:16 -04:00
2021-04-08 15:25:29 -04:00
for_each_btree_key ( & trans , iter , BTREE_ID_inodes ,
2021-05-14 16:56:26 -04:00
POS ( 0 , range_start ) ,
BTREE_ITER_INTENT |
2021-04-20 00:15:44 -04:00
BTREE_ITER_PREFETCH |
BTREE_ITER_ALL_SNAPSHOTS , k , ret ) {
2021-04-21 21:08:49 -04:00
if ( k . k - > p . offset > = range_end )
2017-03-16 22:18:50 -08:00
break ;
2021-10-29 21:14:23 -04:00
if ( ! bkey_is_inode ( k . k ) )
2021-04-08 15:25:29 -04:00
continue ;
2017-03-16 22:18:50 -08:00
2021-10-29 21:14:23 -04:00
BUG_ON ( bch2_inode_unpack ( k , & u ) ) ;
2021-04-21 21:08:49 -04:00
2021-10-29 21:14:23 -04:00
if ( S_ISDIR ( le16_to_cpu ( u . bi_mode ) ) )
continue ;
2017-03-16 22:18:50 -08:00
2021-04-21 21:08:49 -04:00
if ( ! u . bi_nlink )
continue ;
2021-04-20 00:15:44 -04:00
while ( ( cmp_int ( link - > inum , k . k - > p . offset ) ? :
cmp_int ( link - > snapshot , k . k - > p . snapshot ) ) < 0 ) {
2021-04-21 21:08:49 -04:00
link + + ;
BUG_ON ( link > = links - > d + links - > nr ) ;
}
if ( fsck_err_on ( bch2_inode_nlink_get ( & u ) ! = link - > count , c ,
2022-04-16 18:59:58 -04:00
" inode %llu type %s has wrong i_nlink (%u, should be %u) " ,
u . bi_inum , bch2_d_types [ mode_to_type ( u . bi_mode ) ] ,
2021-04-21 21:08:49 -04:00
bch2_inode_nlink_get ( & u ) , link - > count ) ) {
bch2_inode_nlink_set ( & u , link - > count ) ;
2021-09-30 11:09:26 -04:00
ret = write_inode ( & trans , & u , k . k - > p . snapshot ) ;
2021-04-21 21:08:49 -04:00
if ( ret )
bch_err ( c , " error in fsck: error %i updating inode " , ret ) ;
}
2017-03-16 22:18:50 -08:00
}
2021-04-21 21:08:49 -04:00
fsck_err :
2021-08-30 15:18:31 -04:00
bch2_trans_iter_exit ( & trans , & iter ) ;
2019-03-13 20:49:16 -04:00
bch2_trans_exit ( & trans ) ;
2021-04-08 15:25:29 -04:00
if ( ret )
bch_err ( c , " error in fsck: btree error %i while walking inodes " , ret ) ;
2017-03-16 22:18:50 -08:00
2021-04-08 15:25:29 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
}
noinline_for_stack
2021-04-19 22:19:18 -04:00
static int check_nlinks ( struct bch_fs * c )
2017-03-16 22:18:50 -08:00
{
2021-04-21 21:08:49 -04:00
struct nlink_table links = { 0 } ;
2017-03-16 22:18:50 -08:00
u64 this_iter_range_start , next_iter_range_start = 0 ;
int ret = 0 ;
bch_verbose ( c , " checking inode nlinks " ) ;
do {
this_iter_range_start = next_iter_range_start ;
next_iter_range_start = U64_MAX ;
2021-04-21 21:08:49 -04:00
ret = check_nlinks_find_hardlinks ( c , & links ,
this_iter_range_start ,
& next_iter_range_start ) ;
ret = check_nlinks_walk_dirents ( c , & links ,
2017-03-16 22:18:50 -08:00
this_iter_range_start ,
2021-04-21 21:08:49 -04:00
next_iter_range_start ) ;
2017-03-16 22:18:50 -08:00
if ( ret )
break ;
2021-04-21 21:08:49 -04:00
ret = check_nlinks_update_hardlinks ( c , & links ,
2017-03-16 22:18:50 -08:00
this_iter_range_start ,
next_iter_range_start ) ;
if ( ret )
break ;
2021-04-21 21:08:49 -04:00
links . nr = 0 ;
2017-03-16 22:18:50 -08:00
} while ( next_iter_range_start ! = U64_MAX ) ;
2021-04-21 21:08:49 -04:00
kvfree ( links . d ) ;
2017-03-16 22:18:50 -08:00
return ret ;
}
2021-10-19 17:30:16 -04:00
static int fix_reflink_p_key ( struct btree_trans * trans , struct btree_iter * iter )
{
struct bkey_s_c k ;
struct bkey_s_c_reflink_p p ;
struct bkey_i_reflink_p * u ;
int ret ;
k = bch2_btree_iter_peek ( iter ) ;
if ( ! k . k )
return 0 ;
ret = bkey_err ( k ) ;
if ( ret )
return ret ;
if ( k . k - > type ! = KEY_TYPE_reflink_p )
return 0 ;
p = bkey_s_c_to_reflink_p ( k ) ;
2021-10-14 09:54:47 -04:00
if ( ! p . v - > front_pad & & ! p . v - > back_pad )
2021-10-19 17:30:16 -04:00
return 0 ;
u = bch2_trans_kmalloc ( trans , sizeof ( * u ) ) ;
ret = PTR_ERR_OR_ZERO ( u ) ;
if ( ret )
return ret ;
bkey_reassemble ( & u - > k_i , k ) ;
2021-10-14 09:54:47 -04:00
u - > v . front_pad = 0 ;
u - > v . back_pad = 0 ;
2021-10-19 17:30:16 -04:00
2021-10-25 19:30:24 -04:00
return bch2_trans_update ( trans , iter , & u - > k_i , BTREE_TRIGGER_NORUN ) ;
2021-10-19 17:30:16 -04:00
}
2021-10-28 16:16:55 -04:00
noinline_for_stack
2021-10-19 17:30:16 -04:00
static int fix_reflink_p ( struct bch_fs * c )
{
struct btree_trans trans ;
struct btree_iter iter ;
struct bkey_s_c k ;
int ret ;
if ( c - > sb . version > = bcachefs_metadata_version_reflink_p_fix )
return 0 ;
2021-10-28 16:16:55 -04:00
bch_verbose ( c , " fixing reflink_p keys " ) ;
2021-10-19 17:30:16 -04:00
bch2_trans_init ( & trans , c , BTREE_ITER_MAX , 0 ) ;
for_each_btree_key ( & trans , iter , BTREE_ID_extents , POS_MIN ,
BTREE_ITER_INTENT |
BTREE_ITER_PREFETCH |
BTREE_ITER_ALL_SNAPSHOTS , k , ret ) {
if ( k . k - > type = = KEY_TYPE_reflink_p ) {
2022-07-13 05:25:29 -04:00
ret = commit_do ( & trans , NULL , NULL ,
2021-10-19 17:30:16 -04:00
BTREE_INSERT_NOFAIL |
BTREE_INSERT_LAZY_RW ,
fix_reflink_p_key ( & trans , & iter ) ) ;
if ( ret )
break ;
}
}
bch2_trans_iter_exit ( & trans , & iter ) ;
bch2_trans_exit ( & trans ) ;
return ret ;
}
2017-03-16 22:18:50 -08:00
/*
* Checks for inconsistencies that shouldn ' t happen , unless we have a bug .
* Doesn ' t fix them yet , mainly because they haven ' t yet been observed :
*/
2019-04-17 18:21:19 -04:00
int bch2_fsck_full ( struct bch_fs * c )
2017-03-16 22:18:50 -08:00
{
2022-07-16 23:21:15 -04:00
int ret ;
again :
ret = bch2_fs_check_snapshots ( c ) ? :
2022-07-14 01:10:24 -04:00
bch2_fs_check_subvols ( c ) ? :
bch2_delete_dead_snapshots ( c ) ? :
2021-03-16 00:42:25 -04:00
check_inodes ( c , true ) ? :
2021-04-06 21:41:48 -04:00
check_extents ( c ) ? :
2017-03-16 22:18:50 -08:00
check_dirents ( c ) ? :
check_xattrs ( c ) ? :
2021-04-20 00:15:44 -04:00
check_root ( c ) ? :
2021-04-19 22:19:18 -04:00
check_directory_structure ( c ) ? :
2021-10-19 17:30:16 -04:00
check_nlinks ( c ) ? :
fix_reflink_p ( c ) ;
2022-07-16 23:21:15 -04:00
if ( ret = = - NEED_SNAPSHOT_CLEANUP ) {
set_bit ( BCH_FS_HAVE_DELETED_SNAPSHOTS , & c - > flags ) ;
goto again ;
}
return ret ;
2017-03-16 22:18:50 -08:00
}
2019-04-17 18:21:19 -04:00
int bch2_fsck_walk_inodes_only ( struct bch_fs * c )
2017-03-16 22:18:50 -08:00
{
2022-07-14 05:44:10 -04:00
return bch2_fs_check_snapshots ( c ) ? :
bch2_fs_check_subvols ( c ) ? :
bch2_delete_dead_snapshots ( c ) ? :
check_inodes ( c , false ) ;
2017-03-16 22:18:50 -08:00
}