@@ -2618,12 +2618,32 @@ class NamespaceFS {
2618
2618
}
2619
2619
}
2620
2620
2621
+ /**
2622
+ * _delete_path_dirs deletes all the paths in the hierarchy that are empty after a successful delete
2623
+ * 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 -
2624
+ * before deletion of the parent directory -
2625
+ * if the parent directory is a directory object (has CONTENT_DIR xattr) - stop the deletion loop
2626
+ * else - delete the directory - if dir is not empty it will stop at the first non empty dir
2627
+ * 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
2628
+ * therefore the deletion will succeed although we shouldn't delete the directory object
2629
+ * @param {String } file_path
2630
+ * @param {nb.NativeFSContext } fs_context
2631
+ */
2621
2632
async _delete_path_dirs ( file_path , fs_context ) {
2622
2633
try {
2623
- let dir = path . dirname ( file_path ) ;
2624
- while ( dir !== this . bucket_path ) {
2625
- await nb_native ( ) . fs . rmdir ( fs_context , dir ) ;
2626
- dir = path . dirname ( dir ) ;
2634
+ let dir_path = path . dirname ( file_path ) ;
2635
+ const deleted_file_is_dir = file_path . endsWith ( '/' ) ;
2636
+ const deleted_file_is_dir_object = file_path . endsWith ( config . NSFS_FOLDER_OBJECT_NAME ) ;
2637
+ let should_check_dir_path_is_content_dir = ! deleted_file_is_dir && ! deleted_file_is_dir_object ;
2638
+ while ( dir_path !== this . bucket_path ) {
2639
+ if ( should_check_dir_path_is_content_dir ) {
2640
+ const dir_stat = await nb_native ( ) . fs . stat ( fs_context , dir_path ) ;
2641
+ const file_is_disabled_dir_content = dir_stat . xattr && dir_stat . xattr [ XATTR_DIR_CONTENT ] !== undefined ;
2642
+ if ( file_is_disabled_dir_content ) break ;
2643
+ }
2644
+ await nb_native ( ) . fs . rmdir ( fs_context , dir_path ) ;
2645
+ dir_path = path . dirname ( dir_path ) ;
2646
+ should_check_dir_path_is_content_dir = true ;
2627
2647
}
2628
2648
} catch ( err ) {
2629
2649
if ( err . code !== 'ENOTEMPTY' &&
@@ -3068,10 +3088,18 @@ class NamespaceFS {
3068
3088
return res ;
3069
3089
}
3070
3090
3071
- // delete version_id -
3072
- // 1. get version info, if it's empty - return
3073
- // 2. unlink key
3074
- // 3. if version is latest version - promote second latest -> latest
3091
+ /**
3092
+ * delete version_id does the following -
3093
+ * 1. get version info, if it's empty - return
3094
+ * 2. unlink key
3095
+ * 3. if version is latest version - promote second latest -> latest
3096
+ * 4. if it's the latest version - delete the directory hirerachy of the key if it's empty
3097
+ * if it's a past version - delete .versions/ and the directory hirerachy if it's empty
3098
+ * @param {nb.NativeFSContext } fs_context
3099
+ * @param {String } file_path
3100
+ * @param {Object } params
3101
+ * @returns {Promise<{deleted_delete_marker?: string, version_id?: string}> }
3102
+ */
3075
3103
async _delete_version_id ( fs_context , file_path , params ) {
3076
3104
// TODO optimization - GPFS link overrides, no need to unlink before promoting, but if there is nothing to promote we should unlink
3077
3105
const del_obj_version_info = await this . _delete_single_object_versioned ( fs_context , params . key , params . version_id ) ;
0 commit comments