2 * object_id.c - Processing of object ids
4 * This module is part of ntfs-3g library
6 * Copyright (c) 2009 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
42 #include <sys/xattr.h>
45 #ifdef HAVE_SYS_SYSMACROS_H
46 #include <sys/sysmacros.h>
59 #include "object_id.h"
64 * Endianness considerations
66 * According to RFC 4122, GUIDs should be printed with the most
67 * significant byte first, and the six fields be compared individually
68 * for ordering. RFC 4122 does not define the internal representation.
70 * Here we always copy disk images with no endianness change,
71 * and, for indexing, GUIDs are compared as if they were a sequence
72 * of four unsigned 32 bit integers.
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') };
133 #ifdef HAVE_SETXATTR /* extended attributes interface required */
136 * Set the index for a new object id
138 * Returns 0 if success
139 * -1 if failure, explained by errno
142 static int set_object_id_index(ntfs_inode
*ni
, ntfs_index_context
*xo
,
143 const OBJECT_ID_ATTR
*object_id
)
145 struct OBJECT_ID_INDEX indx
;
150 seqn
= ni
->mrec
->sequence_number
;
151 file_id_cpu
= MK_MREF(ni
->mft_no
,le16_to_cpu(seqn
));
152 file_id
= cpu_to_le64(file_id_cpu
);
153 indx
.header
.data_offset
= const_cpu_to_le16(
154 sizeof(INDEX_ENTRY_HEADER
)
155 + sizeof(OBJECT_ID_INDEX_KEY
));
156 indx
.header
.data_length
= const_cpu_to_le16(
157 sizeof(OBJECT_ID_INDEX_DATA
));
158 indx
.header
.reservedV
= const_cpu_to_le32(0);
159 indx
.header
.length
= const_cpu_to_le16(
160 sizeof(struct OBJECT_ID_INDEX
));
161 indx
.header
.key_length
= const_cpu_to_le16(
162 sizeof(OBJECT_ID_INDEX_KEY
));
163 indx
.header
.flags
= const_cpu_to_le16(0);
164 indx
.header
.reserved
= const_cpu_to_le16(0);
166 memcpy(&indx
.key
.object_id
,object_id
,sizeof(GUID
));
168 indx
.data
.file_id
= file_id
;
169 memcpy(&indx
.data
.birth_volume_id
,
170 &object_id
->birth_volume_id
,sizeof(GUID
));
171 memcpy(&indx
.data
.birth_object_id
,
172 &object_id
->birth_object_id
,sizeof(GUID
));
173 memcpy(&indx
.data
.domain_id
,
174 &object_id
->domain_id
,sizeof(GUID
));
175 ntfs_index_ctx_reinit(xo
);
176 return (ntfs_ie_add(xo
,(INDEX_ENTRY
*)&indx
));
179 #endif /* HAVE_SETXATTR */
182 * Open the $Extend/$ObjId file and its index
184 * Return the index context if opened
185 * or NULL if an error occurred (errno tells why)
187 * The index has to be freed and inode closed when not needed any more.
190 static ntfs_index_context
*open_object_id_index(ntfs_volume
*vol
)
195 ntfs_index_context
*xo
;
197 /* do not use path_name_to inode - could reopen root */
198 dir_ni
= ntfs_inode_open(vol
, FILE_Extend
);
199 ni
= (ntfs_inode
*)NULL
;
201 inum
= ntfs_inode_lookup_by_mbsname(dir_ni
,"$ObjId");
203 ni
= ntfs_inode_open(vol
, inum
);
204 ntfs_inode_close(dir_ni
);
207 xo
= ntfs_index_ctx_get(ni
, objid_index_name
, 2);
209 ntfs_inode_close(ni
);
212 xo
= (ntfs_index_context
*)NULL
;
216 #ifdef HAVE_SETXATTR /* extended attributes interface required */
219 * Merge object_id data stored in the index into
220 * a full object_id struct.
222 * returns 0 if merging successful
223 * -1 if no data could be merged. This is generally not an error
226 static int merge_index_data(ntfs_inode
*ni
,
227 const OBJECT_ID_ATTR
*objectid_attr
,
228 OBJECT_ID_ATTR
*full_objectid
)
230 OBJECT_ID_INDEX_KEY key
;
231 struct OBJECT_ID_INDEX
*entry
;
232 ntfs_index_context
*xo
;
237 xo
= open_object_id_index(ni
->vol
);
239 memcpy(&key
.object_id
,objectid_attr
,sizeof(GUID
));
240 if (!ntfs_index_lookup(&key
,
241 sizeof(OBJECT_ID_INDEX_KEY
), xo
)) {
242 entry
= (struct OBJECT_ID_INDEX
*)xo
->entry
;
243 /* make sure inode numbers match */
245 && (MREF(le64_to_cpu(entry
->data
.file_id
))
247 memcpy(&full_objectid
->birth_volume_id
,
248 &entry
->data
.birth_volume_id
,
250 memcpy(&full_objectid
->birth_object_id
,
251 &entry
->data
.birth_object_id
,
253 memcpy(&full_objectid
->domain_id
,
254 &entry
->data
.domain_id
,
260 ntfs_index_ctx_put(xo
);
261 ntfs_inode_close(xoni
);
266 #endif /* HAVE_SETXATTR */
269 * Remove an object id index entry if attribute present
271 * Returns the size of existing object id
272 * (the existing object_d is returned)
273 * -1 if failure, explained by errno
276 static int remove_object_id_index(ntfs_attr
*na
, ntfs_index_context
*xo
,
277 OBJECT_ID_ATTR
*old_attr
)
279 OBJECT_ID_INDEX_KEY key
;
280 struct OBJECT_ID_INDEX
*entry
;
286 /* read the existing object id attribute */
287 size
= ntfs_attr_pread(na
, 0, sizeof(GUID
), old_attr
);
288 if (size
>= (s64
)sizeof(GUID
)) {
289 memcpy(&key
.object_id
,
290 &old_attr
->object_id
,sizeof(GUID
));
291 if (!ntfs_index_lookup(&key
,
292 sizeof(OBJECT_ID_INDEX_KEY
), xo
)) {
293 entry
= (struct OBJECT_ID_INDEX
*)xo
->entry
;
294 memcpy(&old_attr
->birth_volume_id
,
295 &entry
->data
.birth_volume_id
,
297 memcpy(&old_attr
->birth_object_id
,
298 &entry
->data
.birth_object_id
,
300 memcpy(&old_attr
->domain_id
,
301 &entry
->data
.domain_id
,
303 if (ntfs_index_rm(xo
))
314 #ifdef HAVE_SETXATTR /* extended attributes interface required */
317 * Update the object id and index
319 * The object_id attribute should have been created and the
320 * non-duplication of the GUID should have been checked before.
322 * Returns 0 if success
323 * -1 if failure, explained by errno
324 * If could not remove the existing index, nothing is done,
325 * If could not write the new data, no index entry is inserted
326 * If failed to insert the index, data is removed
329 static int update_object_id(ntfs_inode
*ni
, ntfs_index_context
*xo
,
330 const OBJECT_ID_ATTR
*value
, size_t size
)
332 OBJECT_ID_ATTR old_attr
;
340 na
= ntfs_attr_open(ni
, AT_OBJECT_ID
, AT_UNNAMED
, 0);
343 /* remove the existing index entry */
344 oldsize
= remove_object_id_index(na
,xo
,&old_attr
);
348 /* resize attribute */
349 res
= ntfs_attr_truncate(na
, (s64
)sizeof(GUID
));
350 /* write the object_id in attribute */
352 written
= (int)ntfs_attr_pwrite(na
,
353 (s64
)0, (s64
)sizeof(GUID
),
355 if (written
!= (s64
)sizeof(GUID
)) {
356 ntfs_log_error("Failed to update "
362 /* write index part if provided */
364 && ((size
< sizeof(OBJECT_ID_ATTR
))
365 || set_object_id_index(ni
,xo
,value
))) {
367 * If cannot index, try to remove the object
368 * id and log the error. There will be an
369 * inconsistency if removal fails.
372 ntfs_log_error("Failed to index object id."
373 " Possible corruption.\n");
384 * Add a (dummy) object id to an inode if it does not exist
386 * returns 0 if attribute was inserted (or already present)
387 * -1 if adding failed (explained by errno)
390 static int add_object_id(ntfs_inode
*ni
, int flags
)
395 res
= -1; /* default return */
396 if (!ntfs_attr_exist(ni
,AT_OBJECT_ID
, AT_UNNAMED
,0)) {
397 if (!(flags
& XATTR_REPLACE
)) {
399 * no object id attribute : add one,
400 * apparently, this does not feed the new value in
401 * Note : NTFS version must be >= 3
403 if (ni
->vol
->major_ver
>= 3) {
404 res
= ntfs_attr_add(ni
, AT_OBJECT_ID
,
405 AT_UNNAMED
, 0, &dummy
, (s64
)0);
412 if (flags
& XATTR_CREATE
)
420 #endif /* HAVE_SETXATTR */
423 * Delete an object_id index entry
425 * Returns 0 if success
426 * -1 if failure, explained by errno
429 int ntfs_delete_object_id_index(ntfs_inode
*ni
)
431 ntfs_index_context
*xo
;
434 OBJECT_ID_ATTR old_attr
;
438 na
= ntfs_attr_open(ni
, AT_OBJECT_ID
, AT_UNNAMED
, 0);
441 * read the existing object id
444 xo
= open_object_id_index(ni
->vol
);
446 if (remove_object_id_index(na
,xo
,&old_attr
) < 0)
449 ntfs_index_entry_mark_dirty(xo
);
451 ntfs_index_ctx_put(xo
);
452 ntfs_inode_close(xoni
);
459 #ifdef HAVE_SETXATTR /* extended attributes interface required */
462 * Get the ntfs object id into an extended attribute
464 * If present, the object_id from the attribute and the GUIDs
465 * from the index are returned (formatted as OBJECT_ID_ATTR)
467 * Returns the global size (can be 0, 16 or 64)
468 * and the buffer is updated if it is long enough
471 int ntfs_get_ntfs_object_id(ntfs_inode
*ni
, char *value
, size_t size
)
473 OBJECT_ID_ATTR full_objectid
;
474 OBJECT_ID_ATTR
*objectid_attr
;
478 full_size
= 0; /* default to no data and some error to be defined */
480 objectid_attr
= (OBJECT_ID_ATTR
*)ntfs_attr_readall(ni
,
481 AT_OBJECT_ID
,(ntfschar
*)NULL
, 0, &attr_size
);
483 /* restrict to only GUID present in attr */
484 if (attr_size
== sizeof(GUID
)) {
485 memcpy(&full_objectid
.object_id
,
486 objectid_attr
,sizeof(GUID
));
487 full_size
= sizeof(GUID
);
488 /* get data from index, if any */
489 if (!merge_index_data(ni
, objectid_attr
,
491 full_size
= sizeof(OBJECT_ID_ATTR
);
493 if (full_size
<= (s64
)size
) {
495 memcpy(value
,&full_objectid
,
501 /* unexpected size, better return unsupported */
509 return (full_size
? (int)full_size
: -errno
);
513 * Set the object id from an extended attribute
515 * If the size is 64, the attribute and index are set.
516 * else if the size is not less than 16 only the attribute is set.
517 * The object id index is set accordingly.
519 * Returns 0, or -1 if there is a problem
522 int ntfs_set_ntfs_object_id(ntfs_inode
*ni
,
523 const char *value
, size_t size
, int flags
)
525 OBJECT_ID_INDEX_KEY key
;
527 ntfs_index_context
*xo
;
531 if (ni
&& value
&& (size
>= sizeof(GUID
))) {
532 xo
= open_object_id_index(ni
->vol
);
534 /* make sure the GUID was not used somewhere */
535 memcpy(&key
.object_id
, value
, sizeof(GUID
));
536 if (ntfs_index_lookup(&key
,
537 sizeof(OBJECT_ID_INDEX_KEY
), xo
)) {
538 ntfs_index_ctx_reinit(xo
);
539 res
= add_object_id(ni
, flags
);
541 /* update value and index */
542 res
= update_object_id(ni
,xo
,
543 (const OBJECT_ID_ATTR
*)value
,
547 /* GUID is present elsewhere */
552 ntfs_index_entry_mark_dirty(xo
);
554 ntfs_index_ctx_put(xo
);
555 ntfs_inode_close(xoni
);
563 return (res
? -1 : 0);
567 * Remove the object id
569 * Returns 0, or -1 if there is a problem
572 int ntfs_remove_ntfs_object_id(ntfs_inode
*ni
)
578 ntfs_index_context
*xo
;
580 OBJECT_ID_ATTR old_attr
;
585 * open and delete the object id
587 na
= ntfs_attr_open(ni
, AT_OBJECT_ID
,
590 /* first remove index (old object id needed) */
591 xo
= open_object_id_index(ni
->vol
);
593 oldsize
= remove_object_id_index(na
,xo
,
598 /* now remove attribute */
599 res
= ntfs_attr_rm(na
);
601 && (oldsize
> (int)sizeof(GUID
))) {
603 * If we could not remove the
604 * attribute, try to restore the
605 * index and log the error. There
606 * will be an inconsistency if
607 * the reindexing fails.
609 set_object_id_index(ni
, xo
,
612 "Failed to remove object id."
613 " Possible corruption.\n");
618 ntfs_index_entry_mark_dirty(xo
);
620 ntfs_index_ctx_put(xo
);
621 ntfs_inode_close(xoni
);
625 /* avoid errno pollution */
637 return (res
? -1 : 0);
640 #endif /* HAVE_SETXATTR */