• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * object_id.c - Processing of object ids
3  *
4  *	This module is part of ntfs-3g library
5  *
6  * Copyright (c) 2009-2019 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  *	Windows apparently stores the first three fields in little endian
67  *	order, and the last two fields in big endian order.
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 little-endian unsigned 32 bit integers (as Windows
72  *	does it that way.)
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 
110 typedef 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 
118 typedef 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 
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;
129 } ;
130 
131 static ntfschar objid_index_name[] = { const_cpu_to_le16('$'),
132 					 const_cpu_to_le16('O') };
133 
134 /*
135  *			Set the index for a new object id
136  *
137  *	Returns 0 if success
138  *		-1 if failure, explained by errno
139  */
140 
set_object_id_index(ntfs_inode * ni,ntfs_index_context * xo,const OBJECT_ID_ATTR * object_id)141 static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo,
142 			const OBJECT_ID_ATTR *object_id)
143 {
144 	struct OBJECT_ID_INDEX indx;
145 	u64 file_id_cpu;
146 	le64 file_id;
147 	le16 seqn;
148 
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);
164 
165 	memcpy(&indx.key.object_id,object_id,sizeof(GUID));
166 
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));
176 }
177 
178 /*
179  *		Open the $Extend/$ObjId file and its index
180  *
181  *	Return the index context if opened
182  *		or NULL if an error occurred (errno tells why)
183  *
184  *	The index has to be freed and inode closed when not needed any more.
185  */
186 
open_object_id_index(ntfs_volume * vol)187 static ntfs_index_context *open_object_id_index(ntfs_volume *vol)
188 {
189 	u64 inum;
190 	ntfs_inode *ni;
191 	ntfs_inode *dir_ni;
192 	ntfs_index_context *xo;
193 
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;
197 	if (dir_ni) {
198 		inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId");
199 		if (inum != (u64)-1)
200 			ni = ntfs_inode_open(vol, inum);
201 		ntfs_inode_close(dir_ni);
202 	}
203 	if (ni) {
204 		xo = ntfs_index_ctx_get(ni, objid_index_name, 2);
205 		if (!xo) {
206 			ntfs_inode_close(ni);
207 		}
208 	} else
209 		xo = (ntfs_index_context*)NULL;
210 	return (xo);
211 }
212 
213 
214 /*
215  *		Merge object_id data stored in the index into
216  *	a full object_id struct.
217  *
218  *	returns 0 if merging successful
219  *		-1 if no data could be merged. This is generally not an error
220  */
221 
merge_index_data(ntfs_inode * ni,const OBJECT_ID_ATTR * objectid_attr,OBJECT_ID_ATTR * full_objectid)222 static int merge_index_data(ntfs_inode *ni,
223 			const OBJECT_ID_ATTR *objectid_attr,
224 			OBJECT_ID_ATTR *full_objectid)
225 {
226 	OBJECT_ID_INDEX_KEY key;
227 	struct OBJECT_ID_INDEX *entry;
228 	ntfs_index_context *xo;
229 	ntfs_inode *xoni;
230 	int res;
231 
232 	res = -1;
233 	xo = open_object_id_index(ni->vol);
234 	if (xo) {
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 */
240 			if (entry
241 			    && (MREF(le64_to_cpu(entry->data.file_id))
242 					== ni->mft_no)) {
243 				memcpy(&full_objectid->birth_volume_id,
244 						&entry->data.birth_volume_id,
245 						sizeof(GUID));
246 				memcpy(&full_objectid->birth_object_id,
247 						&entry->data.birth_object_id,
248 						sizeof(GUID));
249 				memcpy(&full_objectid->domain_id,
250 						&entry->data.domain_id,
251 						sizeof(GUID));
252 				res = 0;
253 			}
254 		}
255 		xoni = xo->ni;
256 		ntfs_index_ctx_put(xo);
257 		ntfs_inode_close(xoni);
258 	}
259 	return (res);
260 }
261 
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 
remove_object_id_index(ntfs_attr * na,ntfs_index_context * xo,OBJECT_ID_ATTR * old_attr)271 static 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 			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,
291 					sizeof(GUID));
292 				memcpy(&old_attr->birth_object_id,
293 					&entry->data.birth_object_id,
294 					sizeof(GUID));
295 				memcpy(&old_attr->domain_id,
296 					&entry->data.domain_id,
297 					sizeof(GUID));
298 				if (ntfs_index_rm(xo))
299 					ret = -1;
300 			}
301 		} else {
302 			ret = -1;
303 			errno = ENODATA;
304 		}
305 	}
306 	return (ret);
307 }
308 
309 
310 /*
311  *		Update the object id and index
312  *
313  *	The object_id attribute should have been created and the
314  *	non-duplication of the GUID should have been checked before.
315  *
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
321  */
322 
update_object_id(ntfs_inode * ni,ntfs_index_context * xo,const OBJECT_ID_ATTR * value,size_t size)323 static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
324 			const OBJECT_ID_ATTR *value, size_t size)
325 {
326 	OBJECT_ID_ATTR old_attr;
327 	ntfs_attr *na;
328 	int oldsize;
329 	int written;
330 	int res;
331 
332 	res = 0;
333 
334 	na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
335 	if (na) {
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);
339 		if (oldsize < 0)
340 			res = -1;
341 		else {
342 			/* resize attribute */
343 			res = ntfs_attr_truncate(na, (s64)sizeof(GUID));
344 				/* write the object_id in attribute */
345 			if (!res && value) {
346 				written = (int)ntfs_attr_pwrite(na,
347 					(s64)0, (s64)sizeof(GUID),
348 					&value->object_id);
349 				if (written != (s64)sizeof(GUID)) {
350 					ntfs_log_error("Failed to update "
351 							"object id\n");
352 					errno = EIO;
353 					res = -1;
354 				}
355 			}
356 				/* overwrite index data with new value */
357 			memcpy(&old_attr, value,
358 				(size < sizeof(OBJECT_ID_ATTR)
359 					? size : sizeof(OBJECT_ID_ATTR)));
360 			if (!res
361 			    && set_object_id_index(ni,xo,&old_attr)) {
362 				/*
363 				 * If cannot index, try to remove the object
364 				 * id and log the error. There will be an
365 				 * inconsistency if removal fails.
366 				 */
367 				ntfs_attr_rm(na);
368 				ntfs_log_error("Failed to index object id."
369 						" Possible corruption.\n");
370 			}
371 		}
372 		ntfs_attr_close(na);
373 		NInoSetDirty(ni);
374 	} else
375 		res = -1;
376 	return (res);
377 }
378 
379 /*
380  *		Add a (dummy) object id to an inode if it does not exist
381  *
382  *	returns 0 if attribute was inserted (or already present)
383  *		-1 if adding failed (explained by errno)
384  */
385 
add_object_id(ntfs_inode * ni,int flags)386 static int add_object_id(ntfs_inode *ni, int flags)
387 {
388 	int res;
389 	u8 dummy;
390 
391 	res = -1; /* default return */
392 	if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) {
393 		if (!(flags & XATTR_REPLACE)) {
394 			/*
395 			 * no object id attribute : add one,
396 			 * apparently, this does not feed the new value in
397 			 * Note : NTFS version must be >= 3
398 			 */
399 			if (ni->vol->major_ver >= 3) {
400 				res = ntfs_attr_add(ni, AT_OBJECT_ID,
401 						AT_UNNAMED, 0, &dummy, (s64)0);
402 				NInoSetDirty(ni);
403 			} else
404 				errno = EOPNOTSUPP;
405 		} else
406 			errno = ENODATA;
407 	} else {
408 		if (flags & XATTR_CREATE)
409 			errno = EEXIST;
410 		else
411 			res = 0;
412 	}
413 	return (res);
414 }
415 
416 
417 /*
418  *		Delete an object_id index entry
419  *
420  *	Returns 0 if success
421  *		-1 if failure, explained by errno
422  */
423 
ntfs_delete_object_id_index(ntfs_inode * ni)424 int ntfs_delete_object_id_index(ntfs_inode *ni)
425 {
426 	ntfs_index_context *xo;
427 	ntfs_inode *xoni;
428 	ntfs_attr *na;
429 	OBJECT_ID_ATTR old_attr;
430 	int res;
431 
432 	res = 0;
433 	na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
434 	if (na) {
435 			/*
436 			 * read the existing object id
437 			 * and un-index it
438 			 */
439 		xo = open_object_id_index(ni->vol);
440 		if (xo) {
441 			if (remove_object_id_index(na,xo,&old_attr) < 0)
442 				res = -1;
443 			xoni = xo->ni;
444 			ntfs_index_entry_mark_dirty(xo);
445 			NInoSetDirty(xoni);
446 			ntfs_index_ctx_put(xo);
447 			ntfs_inode_close(xoni);
448 		}
449 		ntfs_attr_close(na);
450 	}
451 	return (res);
452 }
453 
454 
455 /*
456  *		Get the ntfs object id into an extended attribute
457  *
458  *	If present, the object_id from the attribute and the GUIDs
459  *	from the index are returned (formatted as OBJECT_ID_ATTR)
460  *
461  *	Returns the global size (can be 0, 16 or 64)
462  *		and the buffer is updated if it is long enough
463  */
464 
ntfs_get_ntfs_object_id(ntfs_inode * ni,char * value,size_t size)465 int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
466 {
467 	OBJECT_ID_ATTR full_objectid;
468 	OBJECT_ID_ATTR *objectid_attr;
469 	s64 attr_size;
470 	int full_size;
471 
472 	full_size = 0;	/* default to no data and some error to be defined */
473 	if (ni) {
474 		objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni,
475 			AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size);
476 		if (objectid_attr) {
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,
484 						&full_objectid)) {
485 					full_size = sizeof(OBJECT_ID_ATTR);
486 				}
487 				if (full_size <= (s64)size) {
488 					if (value)
489 						memcpy(value,&full_objectid,
490 							full_size);
491 					else
492 						errno = EINVAL;
493 				}
494 			} else {
495 			/* unexpected size, better return unsupported */
496 				errno = EOPNOTSUPP;
497 				full_size = 0;
498 			}
499 			free(objectid_attr);
500 		} else
501 			errno = ENODATA;
502 	}
503 	return (full_size ? (int)full_size : -errno);
504 }
505 
506 /*
507  *		Set the object id from an extended attribute
508  *
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.
514  *
515  *	Returns 0, or -1 if there is a problem
516  */
517 
ntfs_set_ntfs_object_id(ntfs_inode * ni,const char * value,size_t size,int flags)518 int ntfs_set_ntfs_object_id(ntfs_inode *ni,
519 			const char *value, size_t size, int flags)
520 {
521 	OBJECT_ID_INDEX_KEY key;
522 	ntfs_inode *xoni;
523 	ntfs_index_context *xo;
524 	int res;
525 
526 	res = 0;
527 	if (ni && value && (size >= sizeof(GUID))) {
528 		xo = open_object_id_index(ni->vol);
529 		if (xo) {
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);
538 				if (!res) {
539 						/* update value and index */
540 					res = update_object_id(ni,xo,
541 						(const OBJECT_ID_ATTR*)value,
542 						size);
543 				}
544 			} else {
545 					/* GUID is present elsewhere */
546 				res = -1;
547 				errno = EEXIST;
548 			}
549 			xoni = xo->ni;
550 			ntfs_index_entry_mark_dirty(xo);
551 			NInoSetDirty(xoni);
552 			ntfs_index_ctx_put(xo);
553 			ntfs_inode_close(xoni);
554 		} else {
555 			res = -1;
556 		}
557 	} else {
558 		errno = EINVAL;
559 		res = -1;
560 	}
561 	return (res ? -1 : 0);
562 }
563 
564 /*
565  *		Remove the object id
566  *
567  *	Returns 0, or -1 if there is a problem
568  */
569 
ntfs_remove_ntfs_object_id(ntfs_inode * ni)570 int ntfs_remove_ntfs_object_id(ntfs_inode *ni)
571 {
572 	int res;
573 	int olderrno;
574 	ntfs_attr *na;
575 	ntfs_inode *xoni;
576 	ntfs_index_context *xo;
577 	int oldsize;
578 	OBJECT_ID_ATTR old_attr;
579 
580 	res = 0;
581 	if (ni) {
582 		/*
583 		 * open and delete the object id
584 		 */
585 		na = ntfs_attr_open(ni, AT_OBJECT_ID,
586 			AT_UNNAMED,0);
587 		if (na) {
588 			/* first remove index (old object id needed) */
589 			xo = open_object_id_index(ni->vol);
590 			if (xo) {
591 				oldsize = remove_object_id_index(na,xo,
592 						&old_attr);
593 				if (oldsize < 0) {
594 					res = -1;
595 				} else {
596 					/* now remove attribute */
597 					res = ntfs_attr_rm(na);
598 					if (res
599 					    && (oldsize > (int)sizeof(GUID))) {
600 					/*
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.
606 					 */
607 						set_object_id_index(ni, xo,
608 							&old_attr);
609 						ntfs_log_error(
610 						"Failed to remove object id."
611 						" Possible corruption.\n");
612 					}
613 				}
614 
615 				xoni = xo->ni;
616 				ntfs_index_entry_mark_dirty(xo);
617 				NInoSetDirty(xoni);
618 				ntfs_index_ctx_put(xo);
619 				ntfs_inode_close(xoni);
620 			}
621 			olderrno = errno;
622 			ntfs_attr_close(na);
623 					/* avoid errno pollution */
624 			if (errno == ENOENT)
625 				errno = olderrno;
626 		} else {
627 			errno = ENODATA;
628 			res = -1;
629 		}
630 		NInoSetDirty(ni);
631 	} else {
632 		errno = EINVAL;
633 		res = -1;
634 	}
635 	return (res ? -1 : 0);
636 }
637