xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c (revision 25a7b01d15612846f332751841da3579db313082)
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 	union {
111 		/* alignment may be needed to evaluate collations */
112 		u32 alignment;
113 		GUID guid;
114 	} object_id;
115 } OBJECT_ID_INDEX_KEY;
116 
117 typedef struct {
118 	le64 file_id;
119 	GUID birth_volume_id;
120 	GUID birth_object_id;
121 	GUID domain_id;
122 } OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA
123 
124 struct OBJECT_ID_INDEX {		/* index entry in $Extend/$ObjId */
125 	INDEX_ENTRY_HEADER header;
126 	OBJECT_ID_INDEX_KEY key;
127 	OBJECT_ID_INDEX_DATA data;
128 } ;
129 
130 static ntfschar objid_index_name[] = { const_cpu_to_le16('$'),
131 					 const_cpu_to_le16('O') };
132 #ifdef HAVE_SETXATTR	/* extended attributes interface required */
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 
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 #endif /* HAVE_SETXATTR */
179 
180 /*
181  *		Open the $Extend/$ObjId file and its index
182  *
183  *	Return the index context if opened
184  *		or NULL if an error occurred (errno tells why)
185  *
186  *	The index has to be freed and inode closed when not needed any more.
187  */
188 
189 static ntfs_index_context *open_object_id_index(ntfs_volume *vol)
190 {
191 	u64 inum;
192 	ntfs_inode *ni;
193 	ntfs_inode *dir_ni;
194 	ntfs_index_context *xo;
195 
196 		/* do not use path_name_to inode - could reopen root */
197 	dir_ni = ntfs_inode_open(vol, FILE_Extend);
198 	ni = (ntfs_inode*)NULL;
199 	if (dir_ni) {
200 		inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId");
201 		if (inum != (u64)-1)
202 			ni = ntfs_inode_open(vol, inum);
203 		ntfs_inode_close(dir_ni);
204 	}
205 	if (ni) {
206 		xo = ntfs_index_ctx_get(ni, objid_index_name, 2);
207 		if (!xo) {
208 			ntfs_inode_close(ni);
209 		}
210 	} else
211 		xo = (ntfs_index_context*)NULL;
212 	return (xo);
213 }
214 
215 #ifdef HAVE_SETXATTR	/* extended attributes interface required */
216 
217 /*
218  *		Merge object_id data stored in the index into
219  *	a full object_id struct.
220  *
221  *	returns 0 if merging successful
222  *		-1 if no data could be merged. This is generally not an error
223  */
224 
225 static int merge_index_data(ntfs_inode *ni,
226 			const OBJECT_ID_ATTR *objectid_attr,
227 			OBJECT_ID_ATTR *full_objectid)
228 {
229 	OBJECT_ID_INDEX_KEY key;
230 	struct OBJECT_ID_INDEX *entry;
231 	ntfs_index_context *xo;
232 	ntfs_inode *xoni;
233 	int res;
234 
235 	res = -1;
236 	xo = open_object_id_index(ni->vol);
237 	if (xo) {
238 		memcpy(&key.object_id,objectid_attr,sizeof(GUID));
239 		if (!ntfs_index_lookup(&key,
240 				sizeof(OBJECT_ID_INDEX_KEY), xo)) {
241 			entry = (struct OBJECT_ID_INDEX*)xo->entry;
242 			/* make sure inode numbers match */
243 			if (entry
244 			    && (MREF(le64_to_cpu(entry->data.file_id))
245 					== ni->mft_no)) {
246 				memcpy(&full_objectid->birth_volume_id,
247 						&entry->data.birth_volume_id,
248 						sizeof(GUID));
249 				memcpy(&full_objectid->birth_object_id,
250 						&entry->data.birth_object_id,
251 						sizeof(GUID));
252 				memcpy(&full_objectid->domain_id,
253 						&entry->data.domain_id,
254 						sizeof(GUID));
255 				res = 0;
256 			}
257 		}
258 		xoni = xo->ni;
259 		ntfs_index_ctx_put(xo);
260 		ntfs_inode_close(xoni);
261 	}
262 	return (res);
263 }
264 
265 #endif /* HAVE_SETXATTR */
266 
267 /*
268  *		Remove an object id index entry if attribute present
269  *
270  *	Returns the size of existing object id
271  *			(the existing object_d is returned)
272  *		-1 if failure, explained by errno
273  */
274 
275 static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo,
276 				OBJECT_ID_ATTR *old_attr)
277 {
278 	OBJECT_ID_INDEX_KEY key;
279 	struct OBJECT_ID_INDEX *entry;
280 	s64 size;
281 	int ret;
282 
283 	ret = na->data_size;
284 	if (ret) {
285 			/* read the existing object id attribute */
286 		size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr);
287 		if (size >= (s64)sizeof(GUID)) {
288 			memcpy(&key.object_id,
289 				&old_attr->object_id,sizeof(GUID));
290 			if (!ntfs_index_lookup(&key,
291 					sizeof(OBJECT_ID_INDEX_KEY), xo)) {
292 				entry = (struct OBJECT_ID_INDEX*)xo->entry;
293 				memcpy(&old_attr->birth_volume_id,
294 					&entry->data.birth_volume_id,
295 					sizeof(GUID));
296 				memcpy(&old_attr->birth_object_id,
297 					&entry->data.birth_object_id,
298 					sizeof(GUID));
299 				memcpy(&old_attr->domain_id,
300 					&entry->data.domain_id,
301 					sizeof(GUID));
302 				if (ntfs_index_rm(xo))
303 					ret = -1;
304 			}
305 		} else {
306 			ret = -1;
307 			errno = ENODATA;
308 		}
309 	}
310 	return (ret);
311 }
312 
313 #ifdef HAVE_SETXATTR	/* extended attributes interface required */
314 
315 /*
316  *		Update the object id and index
317  *
318  *	The object_id attribute should have been created and the
319  *	non-duplication of the GUID should have been checked before.
320  *
321  *	Returns 0 if success
322  *		-1 if failure, explained by errno
323  *	If could not remove the existing index, nothing is done,
324  *	If could not write the new data, no index entry is inserted
325  *	If failed to insert the index, data is removed
326  */
327 
328 static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
329 			const OBJECT_ID_ATTR *value, size_t size)
330 {
331 	OBJECT_ID_ATTR old_attr;
332 	ntfs_attr *na;
333 	int oldsize;
334 	int written;
335 	int res;
336 
337 	res = 0;
338 
339 	na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
340 	if (na) {
341 
342 			/* remove the existing index entry */
343 		oldsize = remove_object_id_index(na,xo,&old_attr);
344 		if (oldsize < 0)
345 			res = -1;
346 		else {
347 			/* resize attribute */
348 			res = ntfs_attr_truncate(na, (s64)sizeof(GUID));
349 				/* write the object_id in attribute */
350 			if (!res && value) {
351 				written = (int)ntfs_attr_pwrite(na,
352 					(s64)0, (s64)sizeof(GUID),
353 					&value->object_id);
354 				if (written != (s64)sizeof(GUID)) {
355 					ntfs_log_error("Failed to update "
356 							"object id\n");
357 					errno = EIO;
358 					res = -1;
359 				}
360 			}
361 				/* write index part if provided */
362 			if (!res
363 			    && ((size < sizeof(OBJECT_ID_ATTR))
364 				 || set_object_id_index(ni,xo,value))) {
365 				/*
366 				 * If cannot index, try to remove the object
367 				 * id and log the error. There will be an
368 				 * inconsistency if removal fails.
369 				 */
370 				ntfs_attr_rm(na);
371 				ntfs_log_error("Failed to index object id."
372 						" Possible corruption.\n");
373 			}
374 		}
375 		ntfs_attr_close(na);
376 		NInoSetDirty(ni);
377 	} else
378 		res = -1;
379 	return (res);
380 }
381 
382 /*
383  *		Add a (dummy) object id to an inode if it does not exist
384  *
385  *	returns 0 if attribute was inserted (or already present)
386  *		-1 if adding failed (explained by errno)
387  */
388 
389 static int add_object_id(ntfs_inode *ni, int flags)
390 {
391 	int res;
392 	u8 dummy;
393 
394 	res = -1; /* default return */
395 	if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) {
396 		if (!(flags & XATTR_REPLACE)) {
397 			/*
398 			 * no object id attribute : add one,
399 			 * apparently, this does not feed the new value in
400 			 * Note : NTFS version must be >= 3
401 			 */
402 			if (ni->vol->major_ver >= 3) {
403 				res = ntfs_attr_add(ni, AT_OBJECT_ID,
404 						AT_UNNAMED, 0, &dummy, (s64)0);
405 				NInoSetDirty(ni);
406 			} else
407 				errno = EOPNOTSUPP;
408 		} else
409 			errno = ENODATA;
410 	} else {
411 		if (flags & XATTR_CREATE)
412 			errno = EEXIST;
413 		else
414 			res = 0;
415 	}
416 	return (res);
417 }
418 
419 #endif /* HAVE_SETXATTR */
420 
421 /*
422  *		Delete an object_id index entry
423  *
424  *	Returns 0 if success
425  *		-1 if failure, explained by errno
426  */
427 
428 int ntfs_delete_object_id_index(ntfs_inode *ni)
429 {
430 	ntfs_index_context *xo;
431 	ntfs_inode *xoni;
432 	ntfs_attr *na;
433 	OBJECT_ID_ATTR old_attr;
434 	int res;
435 
436 	res = 0;
437 	na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
438 	if (na) {
439 			/*
440 			 * read the existing object id
441 			 * and un-index it
442 			 */
443 		xo = open_object_id_index(ni->vol);
444 		if (xo) {
445 			if (remove_object_id_index(na,xo,&old_attr) < 0)
446 				res = -1;
447 			xoni = xo->ni;
448 			ntfs_index_entry_mark_dirty(xo);
449 			NInoSetDirty(xoni);
450 			ntfs_index_ctx_put(xo);
451 			ntfs_inode_close(xoni);
452 		}
453 		ntfs_attr_close(na);
454 	}
455 	return (res);
456 }
457 
458 #ifdef HAVE_SETXATTR	/* extended attributes interface required */
459 
460 /*
461  *		Get the ntfs object id into an extended attribute
462  *
463  *	If present, the object_id from the attribute and the GUIDs
464  *	from the index are returned (formatted as OBJECT_ID_ATTR)
465  *
466  *	Returns the global size (can be 0, 16 or 64)
467  *		and the buffer is updated if it is long enough
468  */
469 
470 int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
471 {
472 	OBJECT_ID_ATTR full_objectid;
473 	OBJECT_ID_ATTR *objectid_attr;
474 	s64 attr_size;
475 	int full_size;
476 
477 	full_size = 0;	/* default to no data and some error to be defined */
478 	if (ni) {
479 		objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni,
480 			AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size);
481 		if (objectid_attr) {
482 				/* restrict to only GUID present in attr */
483 			if (attr_size == sizeof(GUID)) {
484 				memcpy(&full_objectid.object_id,
485 						objectid_attr,sizeof(GUID));
486 				full_size = sizeof(GUID);
487 					/* get data from index, if any */
488 				if (!merge_index_data(ni, objectid_attr,
489 						&full_objectid)) {
490 					full_size = sizeof(OBJECT_ID_ATTR);
491 				}
492 				if (full_size <= (s64)size) {
493 					if (value)
494 						memcpy(value,&full_objectid,
495 							full_size);
496 					else
497 						errno = EINVAL;
498 				}
499 			} else {
500 			/* unexpected size, better return unsupported */
501 				errno = EOPNOTSUPP;
502 				full_size = 0;
503 			}
504 			free(objectid_attr);
505 		} else
506 			errno = ENODATA;
507 	}
508 	return (full_size ? (int)full_size : -errno);
509 }
510 
511 /*
512  *		Set the object id from an extended attribute
513  *
514  *	If the size is 64, the attribute and index are set.
515  *	else if the size is not less than 16 only the attribute is set.
516  *	The object id index is set accordingly.
517  *
518  *	Returns 0, or -1 if there is a problem
519  */
520 
521 int ntfs_set_ntfs_object_id(ntfs_inode *ni,
522 			const char *value, size_t size, int flags)
523 {
524 	OBJECT_ID_INDEX_KEY key;
525 	ntfs_inode *xoni;
526 	ntfs_index_context *xo;
527 	int res;
528 
529 	res = 0;
530 	if (ni && value && (size >= sizeof(GUID))) {
531 		xo = open_object_id_index(ni->vol);
532 		if (xo) {
533 			/* make sure the GUID was not used somewhere */
534 			memcpy(&key.object_id, value, sizeof(GUID));
535 			if (ntfs_index_lookup(&key,
536 					sizeof(OBJECT_ID_INDEX_KEY), xo)) {
537 				ntfs_index_ctx_reinit(xo);
538 				res = add_object_id(ni, flags);
539 				if (!res) {
540 						/* update value and index */
541 					res = update_object_id(ni,xo,
542 						(const OBJECT_ID_ATTR*)value,
543 						size);
544 				}
545 			} else {
546 					/* GUID is present elsewhere */
547 				res = -1;
548 				errno = EEXIST;
549 			}
550 			xoni = xo->ni;
551 			ntfs_index_entry_mark_dirty(xo);
552 			NInoSetDirty(xoni);
553 			ntfs_index_ctx_put(xo);
554 			ntfs_inode_close(xoni);
555 		} else {
556 			res = -1;
557 		}
558 	} else {
559 		errno = EINVAL;
560 		res = -1;
561 	}
562 	return (res ? -1 : 0);
563 }
564 
565 /*
566  *		Remove the object id
567  *
568  *	Returns 0, or -1 if there is a problem
569  */
570 
571 int ntfs_remove_ntfs_object_id(ntfs_inode *ni)
572 {
573 	int res;
574 	int olderrno;
575 	ntfs_attr *na;
576 	ntfs_inode *xoni;
577 	ntfs_index_context *xo;
578 	int oldsize;
579 	OBJECT_ID_ATTR old_attr;
580 
581 	res = 0;
582 	if (ni) {
583 		/*
584 		 * open and delete the object id
585 		 */
586 		na = ntfs_attr_open(ni, AT_OBJECT_ID,
587 			AT_UNNAMED,0);
588 		if (na) {
589 			/* first remove index (old object id needed) */
590 			xo = open_object_id_index(ni->vol);
591 			if (xo) {
592 				oldsize = remove_object_id_index(na,xo,
593 						&old_attr);
594 				if (oldsize < 0) {
595 					res = -1;
596 				} else {
597 					/* now remove attribute */
598 					res = ntfs_attr_rm(na);
599 					if (res
600 					    && (oldsize > (int)sizeof(GUID))) {
601 					/*
602 					 * If we could not remove the
603 					 * attribute, try to restore the
604 					 * index and log the error. There
605 					 * will be an inconsistency if
606 					 * the reindexing fails.
607 					 */
608 						set_object_id_index(ni, xo,
609 							&old_attr);
610 						ntfs_log_error(
611 						"Failed to remove object id."
612 						" Possible corruption.\n");
613 					}
614 				}
615 
616 				xoni = xo->ni;
617 				ntfs_index_entry_mark_dirty(xo);
618 				NInoSetDirty(xoni);
619 				ntfs_index_ctx_put(xo);
620 				ntfs_inode_close(xoni);
621 			}
622 			olderrno = errno;
623 			ntfs_attr_close(na);
624 					/* avoid errno pollution */
625 			if (errno == ENOENT)
626 				errno = olderrno;
627 		} else {
628 			errno = ENODATA;
629 			res = -1;
630 		}
631 		NInoSetDirty(ni);
632 	} else {
633 		errno = EINVAL;
634 		res = -1;
635 	}
636 	return (res ? -1 : 0);
637 }
638 
639 #endif /* HAVE_SETXATTR */
640