summaryrefslogtreecommitdiff
path: root/libntfs-3g/object_id.c (plain)
blob: 555dd13773e90ae1c684b46d8214fa199cf7dadd
1/**
2 * object_id.c - Processing of object ids
3 *
4 * This module is part of ntfs-3g library
5 *
6 * Copyright (c) 2009 Jean-Pierre Andre
7 *
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.
12 *
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.
17 *
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
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#ifdef HAVE_STDLIB_H
29#include <stdlib.h>
30#endif
31#ifdef HAVE_ERRNO_H
32#include <errno.h>
33#endif
34#ifdef HAVE_STRING_H
35#include <string.h>
36#endif
37#ifdef HAVE_SYS_STAT_H
38#include <sys/stat.h>
39#endif
40
41#ifdef HAVE_SETXATTR
42#include <sys/xattr.h>
43#endif
44
45#ifdef HAVE_SYS_SYSMACROS_H
46#include <sys/sysmacros.h>
47#endif
48
49#include "types.h"
50#include "debug.h"
51#include "attrib.h"
52#include "inode.h"
53#include "dir.h"
54#include "volume.h"
55#include "mft.h"
56#include "index.h"
57#include "lcnalloc.h"
58#include "object_id.h"
59#include "logging.h"
60#include "misc.h"
61
62/*
63 * Endianness considerations
64 *
65 * According to RFC 4122, GUIDs should be printed with the most
66 * significant byte first, and the six fields be compared individually
67 * for ordering. RFC 4122 does not define the internal representation.
68 *
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 unsigned 32 bit integers.
72 *
73 * --------------------- begin from RFC 4122 ----------------------
74 * Consider each field of the UUID to be an unsigned integer as shown
75 * in the table in section Section 4.1.2. Then, to compare a pair of
76 * UUIDs, arithmetically compare the corresponding fields from each
77 * UUID in order of significance and according to their data type.
78 * Two UUIDs are equal if and only if all the corresponding fields
79 * are equal.
80 *
81 * UUIDs, as defined in this document, can also be ordered
82 * lexicographically. For a pair of UUIDs, the first one follows the
83 * second if the most significant field in which the UUIDs differ is
84 * greater for the first UUID. The second precedes the first if the
85 * most significant field in which the UUIDs differ is greater for
86 * the second UUID.
87 *
88 * The fields are encoded as 16 octets, with the sizes and order of the
89 * fields defined above, and with each field encoded with the Most
90 * Significant Byte first (known as network byte order). Note that the
91 * field names, particularly for multiplexed fields, follow historical
92 * practice.
93 *
94 * 0 1 2 3
95 * 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
96 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
97 * | time_low |
98 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99 * | time_mid | time_hi_and_version |
100 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101 * |clk_seq_hi_res | clk_seq_low | node (0-1) |
102 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
103 * | node (2-5) |
104 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
105 *
106 * ---------------------- end from RFC 4122 -----------------------
107 */
108
109typedef struct {
110 GUID object_id;
111} OBJECT_ID_INDEX_KEY;
112
113typedef struct {
114 le64 file_id;
115 GUID birth_volume_id;
116 GUID birth_object_id;
117 GUID domain_id;
118} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA
119
120struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */
121 INDEX_ENTRY_HEADER header;
122 OBJECT_ID_INDEX_KEY key;
123 OBJECT_ID_INDEX_DATA data;
124} ;
125
126static ntfschar objid_index_name[] = { const_cpu_to_le16('$'),
127 const_cpu_to_le16('O') };
128#ifdef HAVE_SETXATTR /* extended attributes interface required */
129
130/*
131 * Set the index for a new object id
132 *
133 * Returns 0 if success
134 * -1 if failure, explained by errno
135 */
136
137static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo,
138 const OBJECT_ID_ATTR *object_id)
139{
140 struct OBJECT_ID_INDEX indx;
141 u64 file_id_cpu;
142 le64 file_id;
143 le16 seqn;
144
145 seqn = ni->mrec->sequence_number;
146 file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn));
147 file_id = cpu_to_le64(file_id_cpu);
148 indx.header.data_offset = const_cpu_to_le16(
149 sizeof(INDEX_ENTRY_HEADER)
150 + sizeof(OBJECT_ID_INDEX_KEY));
151 indx.header.data_length = const_cpu_to_le16(
152 sizeof(OBJECT_ID_INDEX_DATA));
153 indx.header.reservedV = const_cpu_to_le32(0);
154 indx.header.length = const_cpu_to_le16(
155 sizeof(struct OBJECT_ID_INDEX));
156 indx.header.key_length = const_cpu_to_le16(
157 sizeof(OBJECT_ID_INDEX_KEY));
158 indx.header.flags = const_cpu_to_le16(0);
159 indx.header.reserved = const_cpu_to_le16(0);
160
161 memcpy(&indx.key.object_id,object_id,sizeof(GUID));
162
163 indx.data.file_id = file_id;
164 memcpy(&indx.data.birth_volume_id,
165 &object_id->birth_volume_id,sizeof(GUID));
166 memcpy(&indx.data.birth_object_id,
167 &object_id->birth_object_id,sizeof(GUID));
168 memcpy(&indx.data.domain_id,
169 &object_id->domain_id,sizeof(GUID));
170 ntfs_index_ctx_reinit(xo);
171 return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx));
172}
173
174#endif /* HAVE_SETXATTR */
175
176/*
177 * Open the $Extend/$ObjId file and its index
178 *
179 * Return the index context if opened
180 * or NULL if an error occurred (errno tells why)
181 *
182 * The index has to be freed and inode closed when not needed any more.
183 */
184
185static ntfs_index_context *open_object_id_index(ntfs_volume *vol)
186{
187 u64 inum;
188 ntfs_inode *ni;
189 ntfs_inode *dir_ni;
190 ntfs_index_context *xo;
191
192 /* do not use path_name_to inode - could reopen root */
193 dir_ni = ntfs_inode_open(vol, FILE_Extend);
194 ni = (ntfs_inode*)NULL;
195 if (dir_ni) {
196 inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId");
197 if (inum != (u64)-1)
198 ni = ntfs_inode_open(vol, inum);
199 ntfs_inode_close(dir_ni);
200 }
201 if (ni) {
202 xo = ntfs_index_ctx_get(ni, objid_index_name, 2);
203 if (!xo) {
204 ntfs_inode_close(ni);
205 }
206 } else
207 xo = (ntfs_index_context*)NULL;
208 return (xo);
209}
210
211#ifdef HAVE_SETXATTR /* extended attributes interface required */
212
213/*
214 * Merge object_id data stored in the index into
215 * a full object_id struct.
216 *
217 * returns 0 if merging successful
218 * -1 if no data could be merged. This is generally not an error
219 */
220
221static int merge_index_data(ntfs_inode *ni,
222 const OBJECT_ID_ATTR *objectid_attr,
223 OBJECT_ID_ATTR *full_objectid)
224{
225 OBJECT_ID_INDEX_KEY key;
226 struct OBJECT_ID_INDEX *entry;
227 ntfs_index_context *xo;
228 ntfs_inode *xoni;
229 int res;
230
231 res = -1;
232 xo = open_object_id_index(ni->vol);
233 if (xo) {
234 memcpy(&key.object_id,objectid_attr,sizeof(GUID));
235 if (!ntfs_index_lookup(&key,
236 sizeof(OBJECT_ID_INDEX_KEY), xo)) {
237 entry = (struct OBJECT_ID_INDEX*)xo->entry;
238 /* make sure inode numbers match */
239 if (entry
240 && (MREF(le64_to_cpu(entry->data.file_id))
241 == ni->mft_no)) {
242 memcpy(&full_objectid->birth_volume_id,
243 &entry->data.birth_volume_id,
244 sizeof(GUID));
245 memcpy(&full_objectid->birth_object_id,
246 &entry->data.birth_object_id,
247 sizeof(GUID));
248 memcpy(&full_objectid->domain_id,
249 &entry->data.domain_id,
250 sizeof(GUID));
251 res = 0;
252 }
253 }
254 xoni = xo->ni;
255 ntfs_index_ctx_put(xo);
256 ntfs_inode_close(xoni);
257 }
258 return (res);
259}
260
261#endif /* HAVE_SETXATTR */
262
263/*
264 * Remove an object id index entry if attribute present
265 *
266 * Returns the size of existing object id
267 * (the existing object_d is returned)
268 * -1 if failure, explained by errno
269 */
270
271static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo,
272 OBJECT_ID_ATTR *old_attr)
273{
274 OBJECT_ID_INDEX_KEY key;
275 struct OBJECT_ID_INDEX *entry;
276 s64 size;
277 int ret;
278
279 ret = na->data_size;
280 if (ret) {
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 size = sizeof(GUID);
287 if (!ntfs_index_lookup(&key,
288 sizeof(OBJECT_ID_INDEX_KEY), xo)) {
289 entry = (struct OBJECT_ID_INDEX*)xo->entry;
290 memcpy(&old_attr->birth_volume_id,
291 &entry->data.birth_volume_id,
292 sizeof(GUID));
293 memcpy(&old_attr->birth_object_id,
294 &entry->data.birth_object_id,
295 sizeof(GUID));
296 memcpy(&old_attr->domain_id,
297 &entry->data.domain_id,
298 sizeof(GUID));
299 size = sizeof(OBJECT_ID_ATTR);
300 if (ntfs_index_rm(xo))
301 ret = -1;
302 }
303 } else {
304 ret = -1;
305 errno = ENODATA;
306 }
307 }
308 return (ret);
309}
310
311#ifdef HAVE_SETXATTR /* extended attributes interface required */
312
313/*
314 * Update the object id and index
315 *
316 * The object_id attribute should have been created and the
317 * non-duplication of the GUID should have been checked before.
318 *
319 * Returns 0 if success
320 * -1 if failure, explained by errno
321 * If could not remove the existing index, nothing is done,
322 * If could not write the new data, no index entry is inserted
323 * If failed to insert the index, data is removed
324 */
325
326static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
327 const OBJECT_ID_ATTR *value, size_t size)
328{
329 OBJECT_ID_ATTR old_attr;
330 ntfs_attr *na;
331 int oldsize;
332 int written;
333 int res;
334
335 res = 0;
336
337 na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
338 if (na) {
339
340 /* remove the existing index entry */
341 oldsize = remove_object_id_index(na,xo,&old_attr);
342 if (oldsize < 0)
343 res = -1;
344 else {
345 /* resize attribute */
346 res = ntfs_attr_truncate(na, (s64)sizeof(GUID));
347 /* write the object_id in attribute */
348 if (!res && value) {
349 written = (int)ntfs_attr_pwrite(na,
350 (s64)0, (s64)sizeof(GUID),
351 &value->object_id);
352 if (written != (s64)sizeof(GUID)) {
353 ntfs_log_error("Failed to update "
354 "object id\n");
355 errno = EIO;
356 res = -1;
357 }
358 }
359 /* write index part if provided */
360 if (!res
361 && ((size < sizeof(OBJECT_ID_ATTR))
362 || set_object_id_index(ni,xo,value))) {
363 /*
364 * If cannot index, try to remove the object
365 * id and log the error. There will be an
366 * inconsistency if removal fails.
367 */
368 ntfs_attr_rm(na);
369 ntfs_log_error("Failed to index object id."
370 " Possible corruption.\n");
371 }
372 }
373 ntfs_attr_close(na);
374 NInoSetDirty(ni);
375 } else
376 res = -1;
377 return (res);
378}
379
380/*
381 * Add a (dummy) object id to an inode if it does not exist
382 *
383 * returns 0 if attribute was inserted (or already present)
384 * -1 if adding failed (explained by errno)
385 */
386
387static int add_object_id(ntfs_inode *ni, int flags)
388{
389 int res;
390 u8 dummy;
391
392 res = -1; /* default return */
393 if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) {
394 if (!(flags & XATTR_REPLACE)) {
395 /*
396 * no object id attribute : add one,
397 * apparently, this does not feed the new value in
398 * Note : NTFS version must be >= 3
399 */
400 if (ni->vol->major_ver >= 3) {
401 res = ntfs_attr_add(ni, AT_OBJECT_ID,
402 AT_UNNAMED, 0, &dummy, (s64)0);
403 NInoSetDirty(ni);
404 } else
405 errno = EOPNOTSUPP;
406 } else
407 errno = ENODATA;
408 } else {
409 if (flags & XATTR_CREATE)
410 errno = EEXIST;
411 else
412 res = 0;
413 }
414 return (res);
415}
416
417#endif /* HAVE_SETXATTR */
418
419/*
420 * Delete an object_id index entry
421 *
422 * Returns 0 if success
423 * -1 if failure, explained by errno
424 */
425
426int ntfs_delete_object_id_index(ntfs_inode *ni)
427{
428 ntfs_index_context *xo;
429 ntfs_inode *xoni;
430 ntfs_attr *na;
431 OBJECT_ID_ATTR old_attr;
432 int res;
433
434 res = 0;
435 na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
436 if (na) {
437 /*
438 * read the existing object id
439 * and un-index it
440 */
441 xo = open_object_id_index(ni->vol);
442 if (xo) {
443 if (remove_object_id_index(na,xo,&old_attr) < 0)
444 res = -1;
445 xoni = xo->ni;
446 ntfs_index_entry_mark_dirty(xo);
447 NInoSetDirty(xoni);
448 ntfs_index_ctx_put(xo);
449 ntfs_inode_close(xoni);
450 }
451 ntfs_attr_close(na);
452 }
453 return (res);
454}
455
456#ifdef HAVE_SETXATTR /* extended attributes interface required */
457
458/*
459 * Get the ntfs object id into an extended attribute
460 *
461 * If present, the object_id from the attribute and the GUIDs
462 * from the index are returned (formatted as OBJECT_ID_ATTR)
463 *
464 * Returns the global size (can be 0, 16 or 64)
465 * and the buffer is updated if it is long enough
466 */
467
468int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
469{
470 OBJECT_ID_ATTR full_objectid;
471 OBJECT_ID_ATTR *objectid_attr;
472 s64 attr_size;
473 int full_size;
474
475 full_size = 0; /* default to no data and some error to be defined */
476 if (ni) {
477 objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni,
478 AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size);
479 if (objectid_attr) {
480 /* restrict to only GUID present in attr */
481 if (attr_size == sizeof(GUID)) {
482 memcpy(&full_objectid.object_id,
483 objectid_attr,sizeof(GUID));
484 full_size = sizeof(GUID);
485 /* get data from index, if any */
486 if (!merge_index_data(ni, objectid_attr,
487 &full_objectid)) {
488 full_size = sizeof(OBJECT_ID_ATTR);
489 }
490 if (full_size <= (s64)size) {
491 if (value)
492 memcpy(value,&full_objectid,
493 full_size);
494 else
495 errno = EINVAL;
496 }
497 } else {
498 /* unexpected size, better return unsupported */
499 errno = EOPNOTSUPP;
500 full_size = 0;
501 }
502 free(objectid_attr);
503 } else
504 errno = ENODATA;
505 }
506 return (full_size ? (int)full_size : -errno);
507}
508
509/*
510 * Set the object id from an extended attribute
511 *
512 * If the size is 64, the attribute and index are set.
513 * else if the size is not less than 16 only the attribute is set.
514 * The object id index is set accordingly.
515 *
516 * Returns 0, or -1 if there is a problem
517 */
518
519int ntfs_set_ntfs_object_id(ntfs_inode *ni,
520 const char *value, size_t size, int flags)
521{
522 OBJECT_ID_INDEX_KEY key;
523 ntfs_inode *xoni;
524 ntfs_index_context *xo;
525 int res;
526
527 res = 0;
528 if (ni && value && (size >= sizeof(GUID))) {
529 xo = open_object_id_index(ni->vol);
530 if (xo) {
531 /* make sure the GUID was not used somewhere */
532 memcpy(&key.object_id, value, sizeof(GUID));
533 if (ntfs_index_lookup(&key,
534 sizeof(OBJECT_ID_INDEX_KEY), xo)) {
535 ntfs_index_ctx_reinit(xo);
536 res = add_object_id(ni, flags);
537 if (!res) {
538 /* update value and index */
539 res = update_object_id(ni,xo,
540 (const OBJECT_ID_ATTR*)value,
541 size);
542 }
543 } else {
544 /* GUID is present elsewhere */
545 res = -1;
546 errno = EEXIST;
547 }
548 xoni = xo->ni;
549 ntfs_index_entry_mark_dirty(xo);
550 NInoSetDirty(xoni);
551 ntfs_index_ctx_put(xo);
552 ntfs_inode_close(xoni);
553 } else {
554 res = -1;
555 }
556 } else {
557 errno = EINVAL;
558 res = -1;
559 }
560 return (res ? -1 : 0);
561}
562
563/*
564 * Remove the object id
565 *
566 * Returns 0, or -1 if there is a problem
567 */
568
569int ntfs_remove_ntfs_object_id(ntfs_inode *ni)
570{
571 int res;
572 int olderrno;
573 ntfs_attr *na;
574 ntfs_inode *xoni;
575 ntfs_index_context *xo;
576 int oldsize;
577 OBJECT_ID_ATTR old_attr;
578
579 res = 0;
580 if (ni) {
581 /*
582 * open and delete the object id
583 */
584 na = ntfs_attr_open(ni, AT_OBJECT_ID,
585 AT_UNNAMED,0);
586 if (na) {
587 /* first remove index (old object id needed) */
588 xo = open_object_id_index(ni->vol);
589 if (xo) {
590 oldsize = remove_object_id_index(na,xo,
591 &old_attr);
592 if (oldsize < 0) {
593 res = -1;
594 } else {
595 /* now remove attribute */
596 res = ntfs_attr_rm(na);
597 if (res
598 && (oldsize > (int)sizeof(GUID))) {
599 /*
600 * If we could not remove the
601 * attribute, try to restore the
602 * index and log the error. There
603 * will be an inconsistency if
604 * the reindexing fails.
605 */
606 set_object_id_index(ni, xo,
607 &old_attr);
608 ntfs_log_error(
609 "Failed to remove object id."
610 " Possible corruption.\n");
611 }
612 }
613
614 xoni = xo->ni;
615 ntfs_index_entry_mark_dirty(xo);
616 NInoSetDirty(xoni);
617 ntfs_index_ctx_put(xo);
618 ntfs_inode_close(xoni);
619 }
620 olderrno = errno;
621 ntfs_attr_close(na);
622 /* avoid errno pollution */
623 if (errno == ENOENT)
624 errno = olderrno;
625 } else {
626 errno = ENODATA;
627 res = -1;
628 }
629 NInoSetDirty(ni);
630 } else {
631 errno = EINVAL;
632 res = -1;
633 }
634 return (res ? -1 : 0);
635}
636
637#endif /* HAVE_SETXATTR */
638