| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Pocessing of object ids |
| * |
| * Part of this file is based on code from the NTFS-3G. |
| * |
| * Copyright (c) 2009-2019 Jean-Pierre Andre |
| * Copyright (c) 2026 LG Electronics Co., Ltd. |
| */ |
| |
| #include "ntfs.h" |
| #include "index.h" |
| #include "object_id.h" |
| |
| struct object_id_index_key { |
| union { |
| u32 alignment; |
| struct guid guid; |
| } object_id; |
| } __packed; |
| |
| struct object_id_index_data { |
| __le64 file_id; |
| struct guid birth_volume_id; |
| struct guid birth_object_id; |
| struct guid domain_id; |
| } __packed; |
| |
| /* Index entry in $Extend/$ObjId */ |
| struct object_id_index { |
| struct index_entry_header header; |
| struct object_id_index_key key; |
| struct object_id_index_data data; |
| } __packed; |
| |
| __le16 objid_index_name[] = {cpu_to_le16('$'), cpu_to_le16('O'), 0}; |
| |
| /* |
| * open_object_id_index - Open the $Extend/$ObjId file and its index |
| * @vol: NTFS volume structure |
| * |
| * Opens the $ObjId system file and retrieves its index context. |
| * |
| * Return: The index context if opened successfully, or NULL if an error |
| * occurred. |
| */ |
| static struct ntfs_index_context *open_object_id_index(struct ntfs_volume *vol) |
| { |
| struct inode *dir_vi, *vi; |
| struct ntfs_inode *dir_ni; |
| struct ntfs_index_context *xo = NULL; |
| struct ntfs_name *name = NULL; |
| u64 mref; |
| int uname_len; |
| __le16 *uname; |
| |
| uname_len = ntfs_nlstoucs(vol, "$ObjId", 6, &uname, |
| NTFS_MAX_NAME_LEN); |
| if (uname_len < 0) |
| return NULL; |
| |
| /* do not use path_name_to inode - could reopen root */ |
| dir_vi = ntfs_iget(vol->sb, FILE_Extend); |
| if (IS_ERR(dir_vi)) { |
| kmem_cache_free(ntfs_name_cache, uname); |
| return NULL; |
| } |
| dir_ni = NTFS_I(dir_vi); |
| |
| mutex_lock_nested(&dir_ni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); |
| mref = ntfs_lookup_inode_by_name(dir_ni, uname, uname_len, &name); |
| mutex_unlock(&dir_ni->mrec_lock); |
| kfree(name); |
| kmem_cache_free(ntfs_name_cache, uname); |
| if (IS_ERR_MREF(mref)) |
| goto put_dir_vi; |
| |
| vi = ntfs_iget(vol->sb, MREF(mref)); |
| if (IS_ERR(vi)) |
| goto put_dir_vi; |
| |
| xo = ntfs_index_ctx_get(NTFS_I(vi), objid_index_name, 2); |
| if (!xo) |
| iput(vi); |
| put_dir_vi: |
| iput(dir_vi); |
| return xo; |
| } |
| |
| |
| /* |
| * remove_object_id_index - Remove an object id index entry if attribute present |
| * @ni: NTFS inode structure containing the attribute |
| * @xo: Index context for the object id index |
| * |
| * Reads the existing object ID attribute and removes it from the index. |
| * |
| * Return: 0 on success, or a negative error code on failure. |
| */ |
| static int remove_object_id_index(struct ntfs_inode *ni, struct ntfs_index_context *xo) |
| { |
| struct object_id_index_key key = {0}; |
| s64 size; |
| |
| if (ni->data_size == 0) |
| return -ENODATA; |
| |
| /* read the existing object id attribute */ |
| size = ntfs_inode_attr_pread(VFS_I(ni), 0, sizeof(struct guid), |
| (char *)&key); |
| if (size != sizeof(struct guid)) |
| return -ENODATA; |
| |
| if (!ntfs_index_lookup(&key, sizeof(struct object_id_index_key), xo)) |
| return ntfs_index_rm(xo); |
| |
| return 0; |
| } |
| |
| /* |
| * ntfs_delete_object_id_index - Delete an object_id index entry |
| * @ni: NTFS inode structure |
| * |
| * Opens the object ID index and removes the entry corresponding to the inode. |
| * |
| * Return: 0 on success, or a negative error code on failure. |
| */ |
| int ntfs_delete_object_id_index(struct ntfs_inode *ni) |
| { |
| struct ntfs_index_context *xo; |
| struct ntfs_inode *xoni; |
| struct inode *attr_vi; |
| int ret = 0; |
| |
| attr_vi = ntfs_attr_iget(VFS_I(ni), AT_OBJECT_ID, AT_UNNAMED, 0); |
| if (IS_ERR(attr_vi)) |
| return PTR_ERR(attr_vi); |
| |
| /* |
| * read the existing object id and un-index it |
| */ |
| xo = open_object_id_index(ni->vol); |
| if (xo) { |
| xoni = xo->idx_ni; |
| mutex_lock_nested(&xoni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); |
| ret = remove_object_id_index(NTFS_I(attr_vi), xo); |
| if (!ret) { |
| ntfs_index_entry_mark_dirty(xo); |
| mark_mft_record_dirty(xoni); |
| } |
| ntfs_index_ctx_put(xo); |
| mutex_unlock(&xoni->mrec_lock); |
| iput(VFS_I(xoni)); |
| } |
| |
| iput(attr_vi); |
| return ret; |
| } |