2 * object_id.c - Processing of object ids
4 * This module is part of ntfs-3g library
6 * Copyright (c) 2009-2019 Jean-Pierre Andre
8 * This program/include file is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program/include file is distributed in the hope that it will be
14 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program (in the main directory of the NTFS-3G
20 * distribution in the file COPYING); if not, write to the Free Software
21 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #ifdef HAVE_SYS_STAT_H
40 #ifdef HAVE_SYS_SYSMACROS_H
41 #include <sys/sysmacros.h>
54 #include "object_id.h"
60 * Endianness considerations
62 * According to RFC 4122, GUIDs should be printed with the most
63 * significant byte first, and the six fields be compared individually
64 * for ordering. RFC 4122 does not define the internal representation.
66 * Windows apparently stores the first three fields in little endian
67 * order, and the last two fields in big endian order.
69 * Here we always copy disk images with no endianness change,
70 * and, for indexing, GUIDs are compared as if they were a sequence
71 * of four little-endian unsigned 32 bit integers (as Windows
74 * --------------------- begin from RFC 4122 ----------------------
75 * Consider each field of the UUID to be an unsigned integer as shown
76 * in the table in section Section 4.1.2. Then, to compare a pair of
77 * UUIDs, arithmetically compare the corresponding fields from each
78 * UUID in order of significance and according to their data type.
79 * Two UUIDs are equal if and only if all the corresponding fields
82 * UUIDs, as defined in this document, can also be ordered
83 * lexicographically. For a pair of UUIDs, the first one follows the
84 * second if the most significant field in which the UUIDs differ is
85 * greater for the first UUID. The second precedes the first if the
86 * most significant field in which the UUIDs differ is greater for
89 * The fields are encoded as 16 octets, with the sizes and order of the
90 * fields defined above, and with each field encoded with the Most
91 * Significant Byte first (known as network byte order). Note that the
92 * field names, particularly for multiplexed fields, follow historical
96 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
97 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
100 * | time_mid | time_hi_and_version |
101 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102 * |clk_seq_hi_res | clk_seq_low | node (0-1) |
103 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
105 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
107 * ---------------------- end from RFC 4122 -----------------------
112 /* alignment may be needed to evaluate collations */
116 } OBJECT_ID_INDEX_KEY
;
120 GUID birth_volume_id
;
121 GUID birth_object_id
;
123 } OBJECT_ID_INDEX_DATA
; // known as OBJ_ID_INDEX_DATA
125 struct OBJECT_ID_INDEX
{ /* index entry in $Extend/$ObjId */
126 INDEX_ENTRY_HEADER header
;
127 OBJECT_ID_INDEX_KEY key
;
128 OBJECT_ID_INDEX_DATA data
;
131 static ntfschar objid_index_name
[] = { const_cpu_to_le16('$'),
132 const_cpu_to_le16('O') };
135 * Set the index for a new object id
137 * Returns 0 if success
138 * -1 if failure, explained by errno
141 static int set_object_id_index(ntfs_inode
*ni
, ntfs_index_context
*xo
,
142 const OBJECT_ID_ATTR
*object_id
)
144 struct OBJECT_ID_INDEX indx
;
149 seqn
= ni
->mrec
->sequence_number
;
150 file_id_cpu
= MK_MREF(ni
->mft_no
,le16_to_cpu(seqn
));
151 file_id
= cpu_to_le64(file_id_cpu
);
152 indx
.header
.data_offset
= const_cpu_to_le16(
153 sizeof(INDEX_ENTRY_HEADER
)
154 + sizeof(OBJECT_ID_INDEX_KEY
));
155 indx
.header
.data_length
= const_cpu_to_le16(
156 sizeof(OBJECT_ID_INDEX_DATA
));
157 indx
.header
.reservedV
= const_cpu_to_le32(0);
158 indx
.header
.length
= const_cpu_to_le16(
159 sizeof(struct OBJECT_ID_INDEX
));
160 indx
.header
.key_length
= const_cpu_to_le16(
161 sizeof(OBJECT_ID_INDEX_KEY
));
162 indx
.header
.flags
= const_cpu_to_le16(0);
163 indx
.header
.reserved
= const_cpu_to_le16(0);
165 memcpy(&indx
.key
.object_id
,object_id
,sizeof(GUID
));
167 indx
.data
.file_id
= file_id
;
168 memcpy(&indx
.data
.birth_volume_id
,
169 &object_id
->birth_volume_id
,sizeof(GUID
));
170 memcpy(&indx
.data
.birth_object_id
,
171 &object_id
->birth_object_id
,sizeof(GUID
));
172 memcpy(&indx
.data
.domain_id
,
173 &object_id
->domain_id
,sizeof(GUID
));
174 ntfs_index_ctx_reinit(xo
);
175 return (ntfs_ie_add(xo
,(INDEX_ENTRY
*)&indx
));
179 * Open the $Extend/$ObjId file and its index
181 * Return the index context if opened
182 * or NULL if an error occurred (errno tells why)
184 * The index has to be freed and inode closed when not needed any more.
187 static ntfs_index_context
*open_object_id_index(ntfs_volume
*vol
)
192 ntfs_index_context
*xo
;
194 /* do not use path_name_to inode - could reopen root */
195 dir_ni
= ntfs_inode_open(vol
, FILE_Extend
);
196 ni
= (ntfs_inode
*)NULL
;
198 inum
= ntfs_inode_lookup_by_mbsname(dir_ni
,"$ObjId");
200 ni
= ntfs_inode_open(vol
, inum
);
201 ntfs_inode_close(dir_ni
);
204 xo
= ntfs_index_ctx_get(ni
, objid_index_name
, 2);
206 ntfs_inode_close(ni
);
209 xo
= (ntfs_index_context
*)NULL
;
215 * Merge object_id data stored in the index into
216 * a full object_id struct.
218 * returns 0 if merging successful
219 * -1 if no data could be merged. This is generally not an error
222 static int merge_index_data(ntfs_inode
*ni
,
223 const OBJECT_ID_ATTR
*objectid_attr
,
224 OBJECT_ID_ATTR
*full_objectid
)
226 OBJECT_ID_INDEX_KEY key
;
227 struct OBJECT_ID_INDEX
*entry
;
228 ntfs_index_context
*xo
;
233 xo
= open_object_id_index(ni
->vol
);
235 memcpy(&key
.object_id
,objectid_attr
,sizeof(GUID
));
236 if (!ntfs_index_lookup(&key
,
237 sizeof(OBJECT_ID_INDEX_KEY
), xo
)) {
238 entry
= (struct OBJECT_ID_INDEX
*)xo
->entry
;
239 /* make sure inode numbers match */
241 && (MREF(le64_to_cpu(entry
->data
.file_id
))
243 memcpy(&full_objectid
->birth_volume_id
,
244 &entry
->data
.birth_volume_id
,
246 memcpy(&full_objectid
->birth_object_id
,
247 &entry
->data
.birth_object_id
,
249 memcpy(&full_objectid
->domain_id
,
250 &entry
->data
.domain_id
,
256 ntfs_index_ctx_put(xo
);
257 ntfs_inode_close(xoni
);
264 * Remove an object id index entry if attribute present
266 * Returns the size of existing object id
267 * (the existing object_d is returned)
268 * -1 if failure, explained by errno
271 static int remove_object_id_index(ntfs_attr
*na
, ntfs_index_context
*xo
,
272 OBJECT_ID_ATTR
*old_attr
)
274 OBJECT_ID_INDEX_KEY key
;
275 struct OBJECT_ID_INDEX
*entry
;
281 /* read the existing object id attribute */
282 size
= ntfs_attr_pread(na
, 0, sizeof(GUID
), old_attr
);
283 if (size
>= (s64
)sizeof(GUID
)) {
284 memcpy(&key
.object_id
,
285 &old_attr
->object_id
,sizeof(GUID
));
286 if (!ntfs_index_lookup(&key
,
287 sizeof(OBJECT_ID_INDEX_KEY
), xo
)) {
288 entry
= (struct OBJECT_ID_INDEX
*)xo
->entry
;
289 memcpy(&old_attr
->birth_volume_id
,
290 &entry
->data
.birth_volume_id
,
292 memcpy(&old_attr
->birth_object_id
,
293 &entry
->data
.birth_object_id
,
295 memcpy(&old_attr
->domain_id
,
296 &entry
->data
.domain_id
,
298 if (ntfs_index_rm(xo
))
311 * Update the object id and index
313 * The object_id attribute should have been created and the
314 * non-duplication of the GUID should have been checked before.
316 * Returns 0 if success
317 * -1 if failure, explained by errno
318 * If could not remove the existing index, nothing is done,
319 * If could not write the new data, no index entry is inserted
320 * If failed to insert the index, data is removed
323 static int update_object_id(ntfs_inode
*ni
, ntfs_index_context
*xo
,
324 const OBJECT_ID_ATTR
*value
, size_t size
)
326 OBJECT_ID_ATTR old_attr
;
334 na
= ntfs_attr_open(ni
, AT_OBJECT_ID
, AT_UNNAMED
, 0);
336 memset(&old_attr
, 0, sizeof(OBJECT_ID_ATTR
));
337 /* remove the existing index entry */
338 oldsize
= remove_object_id_index(na
,xo
,&old_attr
);
342 /* resize attribute */
343 res
= ntfs_attr_truncate(na
, (s64
)sizeof(GUID
));
344 /* write the object_id in attribute */
346 written
= (int)ntfs_attr_pwrite(na
,
347 (s64
)0, (s64
)sizeof(GUID
),
349 if (written
!= (s64
)sizeof(GUID
)) {
350 ntfs_log_error("Failed to update "
356 /* overwrite index data with new value */
357 memcpy(&old_attr
, value
,
358 (size
< sizeof(OBJECT_ID_ATTR
)
359 ? size
: sizeof(OBJECT_ID_ATTR
)));
361 && set_object_id_index(ni
,xo
,&old_attr
)) {
363 * If cannot index, try to remove the object
364 * id and log the error. There will be an
365 * inconsistency if removal fails.
368 ntfs_log_error("Failed to index object id."
369 " Possible corruption.\n");
380 * Add a (dummy) object id to an inode if it does not exist
382 * returns 0 if attribute was inserted (or already present)
383 * -1 if adding failed (explained by errno)
386 static int add_object_id(ntfs_inode
*ni
, int flags
)
391 res
= -1; /* default return */
392 if (!ntfs_attr_exist(ni
,AT_OBJECT_ID
, AT_UNNAMED
,0)) {
393 if (!(flags
& XATTR_REPLACE
)) {
395 * no object id attribute : add one,
396 * apparently, this does not feed the new value in
397 * Note : NTFS version must be >= 3
399 if (ni
->vol
->major_ver
>= 3) {
400 res
= ntfs_attr_add(ni
, AT_OBJECT_ID
,
401 AT_UNNAMED
, 0, &dummy
, (s64
)0);
408 if (flags
& XATTR_CREATE
)
418 * Delete an object_id index entry
420 * Returns 0 if success
421 * -1 if failure, explained by errno
424 int ntfs_delete_object_id_index(ntfs_inode
*ni
)
426 ntfs_index_context
*xo
;
429 OBJECT_ID_ATTR old_attr
;
433 na
= ntfs_attr_open(ni
, AT_OBJECT_ID
, AT_UNNAMED
, 0);
436 * read the existing object id
439 xo
= open_object_id_index(ni
->vol
);
441 if (remove_object_id_index(na
,xo
,&old_attr
) < 0)
444 ntfs_index_entry_mark_dirty(xo
);
446 ntfs_index_ctx_put(xo
);
447 ntfs_inode_close(xoni
);
456 * Get the ntfs object id into an extended attribute
458 * If present, the object_id from the attribute and the GUIDs
459 * from the index are returned (formatted as OBJECT_ID_ATTR)
461 * Returns the global size (can be 0, 16 or 64)
462 * and the buffer is updated if it is long enough
465 int ntfs_get_ntfs_object_id(ntfs_inode
*ni
, char *value
, size_t size
)
467 OBJECT_ID_ATTR full_objectid
;
468 OBJECT_ID_ATTR
*objectid_attr
;
472 full_size
= 0; /* default to no data and some error to be defined */
474 objectid_attr
= (OBJECT_ID_ATTR
*)ntfs_attr_readall(ni
,
475 AT_OBJECT_ID
,(ntfschar
*)NULL
, 0, &attr_size
);
477 /* restrict to only GUID present in attr */
478 if (attr_size
== sizeof(GUID
)) {
479 memcpy(&full_objectid
.object_id
,
480 objectid_attr
,sizeof(GUID
));
481 full_size
= sizeof(GUID
);
482 /* get data from index, if any */
483 if (!merge_index_data(ni
, objectid_attr
,
485 full_size
= sizeof(OBJECT_ID_ATTR
);
487 if (full_size
<= (s64
)size
) {
489 memcpy(value
,&full_objectid
,
495 /* unexpected size, better return unsupported */
503 return (full_size
? (int)full_size
: -errno
);
507 * Set the object id from an extended attribute
509 * The first 16 bytes are the new object id, they can be followed
510 * by the birth volume id, the birth object id and the domain id.
511 * If they are not present, their previous value is kept.
512 * Only the object id is stored into the attribute, all the fields
513 * are stored into the index.
515 * Returns 0, or -1 if there is a problem
518 int ntfs_set_ntfs_object_id(ntfs_inode
*ni
,
519 const char *value
, size_t size
, int flags
)
521 OBJECT_ID_INDEX_KEY key
;
523 ntfs_index_context
*xo
;
527 if (ni
&& value
&& (size
>= sizeof(GUID
))) {
528 xo
= open_object_id_index(ni
->vol
);
530 /* make sure the GUID was not used elsewhere */
531 memcpy(&key
.object_id
, value
, sizeof(GUID
));
532 if ((ntfs_index_lookup(&key
,
533 sizeof(OBJECT_ID_INDEX_KEY
), xo
))
534 || (MREF_LE(((struct OBJECT_ID_INDEX
*)xo
->entry
)
535 ->data
.file_id
) == ni
->mft_no
)) {
536 ntfs_index_ctx_reinit(xo
);
537 res
= add_object_id(ni
, flags
);
539 /* update value and index */
540 res
= update_object_id(ni
,xo
,
541 (const OBJECT_ID_ATTR
*)value
,
545 /* GUID is present elsewhere */
550 ntfs_index_entry_mark_dirty(xo
);
552 ntfs_index_ctx_put(xo
);
553 ntfs_inode_close(xoni
);
561 return (res
? -1 : 0);
565 * Remove the object id
567 * Returns 0, or -1 if there is a problem
570 int ntfs_remove_ntfs_object_id(ntfs_inode
*ni
)
576 ntfs_index_context
*xo
;
578 OBJECT_ID_ATTR old_attr
;
583 * open and delete the object id
585 na
= ntfs_attr_open(ni
, AT_OBJECT_ID
,
588 /* first remove index (old object id needed) */
589 xo
= open_object_id_index(ni
->vol
);
591 oldsize
= remove_object_id_index(na
,xo
,
596 /* now remove attribute */
597 res
= ntfs_attr_rm(na
);
599 && (oldsize
> (int)sizeof(GUID
))) {
601 * If we could not remove the
602 * attribute, try to restore the
603 * index and log the error. There
604 * will be an inconsistency if
605 * the reindexing fails.
607 set_object_id_index(ni
, xo
,
610 "Failed to remove object id."
611 " Possible corruption.\n");
616 ntfs_index_entry_mark_dirty(xo
);
618 ntfs_index_ctx_put(xo
);
619 ntfs_inode_close(xoni
);
623 /* avoid errno pollution */
635 return (res
? -1 : 0);