xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c (revision 7a74a5df454197933bc6e80a542102362ee98703)
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 
109 typedef struct {
110 	GUID object_id;
111 } OBJECT_ID_INDEX_KEY;
112 
113 typedef 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 
120 struct 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 
126 static 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 
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 #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 
185 static 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 
221 static 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 
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 			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 
326 static 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 
387 static 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 
426 int 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 
468 int 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 
519 int 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 
569 int 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