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