xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 
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 #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 
142 static 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 
190 static 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 
226 static 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 
276 static 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 
329 static 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 
390 static 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 
429 int 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 
471 int 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 
522 int 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 
572 int 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