@@ -2565,12 +2565,32 @@ class NamespaceFS {
2565
2565
}
2566
2566
}
2567
2567
2568
+ /**
2569
+ * _delete_path_dirs deletes all the paths in the hierarchy that are empty after a successful delete
2570
+ * if the original file_path to be deleted is a regular object which means file_path is not a directory and it's not a directory object path -
2571
+ * before deletion of the parent directory -
2572
+ * if the parent directory is a directory object (has CONTENT_DIR xattr) - stop the deletion loop
2573
+ * else - delete the directory - if dir is not empty it will stop at the first non empty dir
2574
+ * NOTE - the directory object check is needed because when object size is zero we won't create a .folder file and the dir will be empty
2575
+ * therefore the deletion will succeed although we shouldn't delete the directory object
2576
+ * @param {String } file_path
2577
+ * @param {nb.NativeFSContext } fs_context
2578
+ */
2568
2579
async _delete_path_dirs ( file_path , fs_context ) {
2569
2580
try {
2570
- let dir = path . dirname ( file_path ) ;
2571
- while ( dir !== this . bucket_path ) {
2572
- await nb_native ( ) . fs . rmdir ( fs_context , dir ) ;
2573
- dir = path . dirname ( dir ) ;
2581
+ let dir_path = path . dirname ( file_path ) ;
2582
+ const deleted_file_is_dir = file_path . endsWith ( '/' ) ;
2583
+ const deleted_file_is_dir_object = file_path . endsWith ( config . NSFS_FOLDER_OBJECT_NAME ) ;
2584
+ let should_check_dir_path_is_content_dir = ! deleted_file_is_dir && ! deleted_file_is_dir_object ;
2585
+ while ( dir_path !== this . bucket_path ) {
2586
+ if ( should_check_dir_path_is_content_dir ) {
2587
+ const dir_stat = await nb_native ( ) . fs . stat ( fs_context , dir_path ) ;
2588
+ const file_is_disabled_dir_content = dir_stat . xattr && dir_stat . xattr [ XATTR_DIR_CONTENT ] !== undefined ;
2589
+ if ( file_is_disabled_dir_content ) break ;
2590
+ }
2591
+ await nb_native ( ) . fs . rmdir ( fs_context , dir_path ) ;
2592
+ dir_path = path . dirname ( dir_path ) ;
2593
+ should_check_dir_path_is_content_dir = true ;
2574
2594
}
2575
2595
} catch ( err ) {
2576
2596
if ( err . code !== 'ENOTEMPTY' &&
@@ -2999,10 +3019,18 @@ class NamespaceFS {
2999
3019
return res ;
3000
3020
}
3001
3021
3002
- // delete version_id -
3003
- // 1. get version info, if it's empty - return
3004
- // 2. unlink key
3005
- // 3. if version is latest version - promote second latest -> latest
3022
+ /**
3023
+ * delete version_id does the following -
3024
+ * 1. get version info, if it's empty - return
3025
+ * 2. unlink key
3026
+ * 3. if version is latest version - promote second latest -> latest
3027
+ * 4. if it's the latest version - delete the directory hirerachy of the key if it's empty
3028
+ * if it's a past version - delete .versions/ and the directory hirerachy if it's empty
3029
+ * @param {nb.NativeFSContext } fs_context
3030
+ * @param {String } file_path
3031
+ * @param {Object } params
3032
+ * @returns {Promise<{deleted_delete_marker?: string, version_id?: string}> }
3033
+ */
3006
3034
async _delete_version_id ( fs_context , file_path , params ) {
3007
3035
// TODO optimization - GPFS link overrides, no need to unlink before promoting, but if there is nothing to promote we should unlink
3008
3036
const del_obj_version_info = await this . _delete_single_object_versioned ( fs_context , params . key , params . version_id ) ;
0 commit comments