// 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; }