summaryrefslogtreecommitdiff
path: root/libntfs-3g/object_id.c (plain)
blob: 299357e56241408b6882b599c6b334382d4a487e
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 "compat.h"
50#include "types.h"
51#include "debug.h"
52#include "attrib.h"
53#include "inode.h"
54#include "dir.h"
55#include "volume.h"
56#include "mft.h"
57#include "index.h"
58#include "lcnalloc.h"
59#include "object_id.h"
60#include "logging.h"
61#include "misc.h"
62
63/*
64 * Endianness considerations
65 *
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.
69 *
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.
73 *
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
80 * are equal.
81 *
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
87 * the second UUID.
88 *
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
93 * practice.
94 *
95 * 0 1 2 3
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98 * | time_low |
99 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
100 * | time_mid | time_hi_and_version |
101 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102 * |clk_seq_hi_res | clk_seq_low | node (0-1) |
103 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104 * | node (2-5) |
105 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
106 *
107 * ---------------------- end from RFC 4122 -----------------------
108 */
109
110typedef struct {
111 union {
112 /* alignment may be needed to evaluate collations */
113 u32 alignment;
114 GUID guid;
115 } object_id;
116} OBJECT_ID_INDEX_KEY;
117
118typedef struct {
119 le64 file_id;
120 GUID birth_volume_id;
121 GUID birth_object_id;
122 GUID domain_id;
123} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA
124
125struct 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;
129} ;
130
131static ntfschar objid_index_name[] = { const_cpu_to_le16('$'),
132 const_cpu_to_le16('O') };
133#ifdef HAVE_SETXATTR /* extended attributes interface required */
134
135/*
136 * Set the index for a new object id
137 *
138 * Returns 0 if success
139 * -1 if failure, explained by errno
140 */
141
142static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo,
143 const OBJECT_ID_ATTR *object_id)
144{
145 struct OBJECT_ID_INDEX indx;
146 u64 file_id_cpu;
147 le64 file_id;
148 le16 seqn;
149
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);
165
166 memcpy(&indx.key.object_id,object_id,sizeof(GUID));
167
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));
177}
178
179#endif /* HAVE_SETXATTR */
180
181/*
182 * Open the $Extend/$ObjId file and its index
183 *
184 * Return the index context if opened
185 * or NULL if an error occurred (errno tells why)
186 *
187 * The index has to be freed and inode closed when not needed any more.
188 */
189
190static ntfs_index_context *open_object_id_index(ntfs_volume *vol)
191{
192 u64 inum;
193 ntfs_inode *ni;
194 ntfs_inode *dir_ni;
195 ntfs_index_context *xo;
196
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;
200 if (dir_ni) {
201 inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId");
202 if (inum != (u64)-1)
203 ni = ntfs_inode_open(vol, inum);
204 ntfs_inode_close(dir_ni);
205 }
206 if (ni) {
207 xo = ntfs_index_ctx_get(ni, objid_index_name, 2);
208 if (!xo) {
209 ntfs_inode_close(ni);
210 }
211 } else
212 xo = (ntfs_index_context*)NULL;
213 return (xo);
214}
215
216#ifdef HAVE_SETXATTR /* extended attributes interface required */
217
218/*
219 * Merge object_id data stored in the index into
220 * a full object_id struct.
221 *
222 * returns 0 if merging successful
223 * -1 if no data could be merged. This is generally not an error
224 */
225
226static int merge_index_data(ntfs_inode *ni,
227 const OBJECT_ID_ATTR *objectid_attr,
228 OBJECT_ID_ATTR *full_objectid)
229{
230 OBJECT_ID_INDEX_KEY key;
231 struct OBJECT_ID_INDEX *entry;
232 ntfs_index_context *xo;
233 ntfs_inode *xoni;
234 int res;
235
236 res = -1;
237 xo = open_object_id_index(ni->vol);
238 if (xo) {
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 */
244 if (entry
245 && (MREF(le64_to_cpu(entry->data.file_id))
246 == ni->mft_no)) {
247 memcpy(&full_objectid->birth_volume_id,
248 &entry->data.birth_volume_id,
249 sizeof(GUID));
250 memcpy(&full_objectid->birth_object_id,
251 &entry->data.birth_object_id,
252 sizeof(GUID));
253 memcpy(&full_objectid->domain_id,
254 &entry->data.domain_id,
255 sizeof(GUID));
256 res = 0;
257 }
258 }
259 xoni = xo->ni;
260 ntfs_index_ctx_put(xo);
261 ntfs_inode_close(xoni);
262 }
263 return (res);
264}
265
266#endif /* HAVE_SETXATTR */
267
268/*
269 * Remove an object id index entry if attribute present
270 *
271 * Returns the size of existing object id
272 * (the existing object_d is returned)
273 * -1 if failure, explained by errno
274 */
275
276static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo,
277 OBJECT_ID_ATTR *old_attr)
278{
279 OBJECT_ID_INDEX_KEY key;
280 struct OBJECT_ID_INDEX *entry;
281 s64 size;
282 int ret;
283
284 ret = na->data_size;
285 if (ret) {
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,
296 sizeof(GUID));
297 memcpy(&old_attr->birth_object_id,
298 &entry->data.birth_object_id,
299 sizeof(GUID));
300 memcpy(&old_attr->domain_id,
301 &entry->data.domain_id,
302 sizeof(GUID));
303 if (ntfs_index_rm(xo))
304 ret = -1;
305 }
306 } else {
307 ret = -1;
308 errno = ENODATA;
309 }
310 }
311 return (ret);
312}
313
314#ifdef HAVE_SETXATTR /* extended attributes interface required */
315
316/*
317 * Update the object id and index
318 *
319 * The object_id attribute should have been created and the
320 * non-duplication of the GUID should have been checked before.
321 *
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
327 */
328
329static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
330 const OBJECT_ID_ATTR *value, size_t size)
331{
332 OBJECT_ID_ATTR old_attr;
333 ntfs_attr *na;
334 int oldsize;
335 int written;
336 int res;
337
338 res = 0;
339
340 na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
341 if (na) {
342
343 /* remove the existing index entry */
344 oldsize = remove_object_id_index(na,xo,&old_attr);
345 if (oldsize < 0)
346 res = -1;
347 else {
348 /* resize attribute */
349 res = ntfs_attr_truncate(na, (s64)sizeof(GUID));
350 /* write the object_id in attribute */
351 if (!res && value) {
352 written = (int)ntfs_attr_pwrite(na,
353 (s64)0, (s64)sizeof(GUID),
354 &value->object_id);
355 if (written != (s64)sizeof(GUID)) {
356 ntfs_log_error("Failed to update "
357 "object id\n");
358 errno = EIO;
359 res = -1;
360 }
361 }
362 /* write index part if provided */
363 if (!res
364 && ((size < sizeof(OBJECT_ID_ATTR))
365 || set_object_id_index(ni,xo,value))) {
366 /*
367 * If cannot index, try to remove the object
368 * id and log the error. There will be an
369 * inconsistency if removal fails.
370 */
371 ntfs_attr_rm(na);
372 ntfs_log_error("Failed to index object id."
373 " Possible corruption.\n");
374 }
375 }
376 ntfs_attr_close(na);
377 NInoSetDirty(ni);
378 } else
379 res = -1;
380 return (res);
381}
382
383/*
384 * Add a (dummy) object id to an inode if it does not exist
385 *
386 * returns 0 if attribute was inserted (or already present)
387 * -1 if adding failed (explained by errno)
388 */
389
390static int add_object_id(ntfs_inode *ni, int flags)
391{
392 int res;
393 u8 dummy;
394
395 res = -1; /* default return */
396 if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) {
397 if (!(flags & XATTR_REPLACE)) {
398 /*
399 * no object id attribute : add one,
400 * apparently, this does not feed the new value in
401 * Note : NTFS version must be >= 3
402 */
403 if (ni->vol->major_ver >= 3) {
404 res = ntfs_attr_add(ni, AT_OBJECT_ID,
405 AT_UNNAMED, 0, &dummy, (s64)0);
406 NInoSetDirty(ni);
407 } else
408 errno = EOPNOTSUPP;
409 } else
410 errno = ENODATA;
411 } else {
412 if (flags & XATTR_CREATE)
413 errno = EEXIST;
414 else
415 res = 0;
416 }
417 return (res);
418}
419
420#endif /* HAVE_SETXATTR */
421
422/*
423 * Delete an object_id index entry
424 *
425 * Returns 0 if success
426 * -1 if failure, explained by errno
427 */
428
429int ntfs_delete_object_id_index(ntfs_inode *ni)
430{
431 ntfs_index_context *xo;
432 ntfs_inode *xoni;
433 ntfs_attr *na;
434 OBJECT_ID_ATTR old_attr;
435 int res;
436
437 res = 0;
438 na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
439 if (na) {
440 /*
441 * read the existing object id
442 * and un-index it
443 */
444 xo = open_object_id_index(ni->vol);
445 if (xo) {
446 if (remove_object_id_index(na,xo,&old_attr) < 0)
447 res = -1;
448 xoni = xo->ni;
449 ntfs_index_entry_mark_dirty(xo);
450 NInoSetDirty(xoni);
451 ntfs_index_ctx_put(xo);
452 ntfs_inode_close(xoni);
453 }
454 ntfs_attr_close(na);
455 }
456 return (res);
457}
458
459#ifdef HAVE_SETXATTR /* extended attributes interface required */
460
461/*
462 * Get the ntfs object id into an extended attribute
463 *
464 * If present, the object_id from the attribute and the GUIDs
465 * from the index are returned (formatted as OBJECT_ID_ATTR)
466 *
467 * Returns the global size (can be 0, 16 or 64)
468 * and the buffer is updated if it is long enough
469 */
470
471int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
472{
473 OBJECT_ID_ATTR full_objectid;
474 OBJECT_ID_ATTR *objectid_attr;
475 s64 attr_size;
476 int full_size;
477
478 full_size = 0; /* default to no data and some error to be defined */
479 if (ni) {
480 objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni,
481 AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size);
482 if (objectid_attr) {
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,
490 &full_objectid)) {
491 full_size = sizeof(OBJECT_ID_ATTR);
492 }
493 if (full_size <= (s64)size) {
494 if (value)
495 memcpy(value,&full_objectid,
496 full_size);
497 else
498 errno = EINVAL;
499 }
500 } else {
501 /* unexpected size, better return unsupported */
502 errno = EOPNOTSUPP;
503 full_size = 0;
504 }
505 free(objectid_attr);
506 } else
507 errno = ENODATA;
508 }
509 return (full_size ? (int)full_size : -errno);
510}
511
512/*
513 * Set the object id from an extended attribute
514 *
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.
518 *
519 * Returns 0, or -1 if there is a problem
520 */
521
522int ntfs_set_ntfs_object_id(ntfs_inode *ni,
523 const char *value, size_t size, int flags)
524{
525 OBJECT_ID_INDEX_KEY key;
526 ntfs_inode *xoni;
527 ntfs_index_context *xo;
528 int res;
529
530 res = 0;
531 if (ni && value && (size >= sizeof(GUID))) {
532 xo = open_object_id_index(ni->vol);
533 if (xo) {
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);
540 if (!res) {
541 /* update value and index */
542 res = update_object_id(ni,xo,
543 (const OBJECT_ID_ATTR*)value,
544 size);
545 }
546 } else {
547 /* GUID is present elsewhere */
548 res = -1;
549 errno = EEXIST;
550 }
551 xoni = xo->ni;
552 ntfs_index_entry_mark_dirty(xo);
553 NInoSetDirty(xoni);
554 ntfs_index_ctx_put(xo);
555 ntfs_inode_close(xoni);
556 } else {
557 res = -1;
558 }
559 } else {
560 errno = EINVAL;
561 res = -1;
562 }
563 return (res ? -1 : 0);
564}
565
566/*
567 * Remove the object id
568 *
569 * Returns 0, or -1 if there is a problem
570 */
571
572int ntfs_remove_ntfs_object_id(ntfs_inode *ni)
573{
574 int res;
575 int olderrno;
576 ntfs_attr *na;
577 ntfs_inode *xoni;
578 ntfs_index_context *xo;
579 int oldsize;
580 OBJECT_ID_ATTR old_attr;
581
582 res = 0;
583 if (ni) {
584 /*
585 * open and delete the object id
586 */
587 na = ntfs_attr_open(ni, AT_OBJECT_ID,
588 AT_UNNAMED,0);
589 if (na) {
590 /* first remove index (old object id needed) */
591 xo = open_object_id_index(ni->vol);
592 if (xo) {
593 oldsize = remove_object_id_index(na,xo,
594 &old_attr);
595 if (oldsize < 0) {
596 res = -1;
597 } else {
598 /* now remove attribute */
599 res = ntfs_attr_rm(na);
600 if (res
601 && (oldsize > (int)sizeof(GUID))) {
602 /*
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.
608 */
609 set_object_id_index(ni, xo,
610 &old_attr);
611 ntfs_log_error(
612 "Failed to remove object id."
613 " Possible corruption.\n");
614 }
615 }
616
617 xoni = xo->ni;
618 ntfs_index_entry_mark_dirty(xo);
619 NInoSetDirty(xoni);
620 ntfs_index_ctx_put(xo);
621 ntfs_inode_close(xoni);
622 }
623 olderrno = errno;
624 ntfs_attr_close(na);
625 /* avoid errno pollution */
626 if (errno == ENOENT)
627 errno = olderrno;
628 } else {
629 errno = ENODATA;
630 res = -1;
631 }
632 NInoSetDirty(ni);
633 } else {
634 errno = EINVAL;
635 res = -1;
636 }
637 return (res ? -1 : 0);
638}
639
640#endif /* HAVE_SETXATTR */
641