1 /* Reverse engineered functions in more or less modified form. find_attr()
2 * is quite heavily modified but should be functionally equivalent to original.
3 * lookup and lookup_external are less modified. Both should be functionally
4 * equivalent to originals. */
7 * attr_search_context - used in attribute search functions
8 * @mrec: buffer containing mft record to search
9 * @attr: attribute record in @mrec where to begin/continue search
10 * @alist_mrec: mft record containing attribute list (i.e. base mft record)
11 * @alist_attr: attribute list attribute record
12 * @alist_val: attribute list value (if alist is resident in @alist_mrec)
13 * @alist_val_end: end of attribute list value + 1
14 * @alist_val_len: length of attribute list in bytes
15 * @is_first: if true lookup_attr() begins search with @attr, else after @attr
17 * Structure must be initialized to zero before the first call to one of the
18 * attribute search functions. If the mft record in which to search has already
19 * been loaded into memory, then initialize @base and @mrec to point to it,
20 * @attr to point to the first attribute within @mrec, and set @is_first to
23 * @is_first is only honoured in lookup_attr() and only when called with @mrec
24 * not NULL. Then, if @is_first is TRUE, lookup_attr() begins the search with
25 * @attr. If @is_first is FALSE, lookup_attr() begins the search after @attr.
26 * This is so that, after the first call to lookup_attr(), we can call
27 * lookup_attr() again, without any modification of the search context, to
28 * automagically get the next matching attribute.
30 * In contrast, find_attr() ignores @is_first and always begins the search with
31 * @attr. find_attr() shouldn't really be called directly; it is just for
32 * internal use. FIXME: Might want to change this behaviour later, but not
33 * before I am finished with lookup_external_attr(). (AIA)
41 MFT_RECORD *alist_mrec;
42 ATTR_RECORD *alist_attr;
43 ATTR_LIST_ENTRY *alist_val;
44 ATTR_LIST_ENTRY *alist_val_end;
46 IS_FIRST_BOOL is_first;
48 } attr_search_context;
50 BOOL attr_find(const ntfs_volume *vol, const ATTR_TYPES type,
51 const wchar_t *name, const u32 name_len,
52 const IGNORE_CASE_BOOL ic, const u8 *val, const u32 val_len,
53 ntfs_attr_search_ctx *ctx)
58 if (!vol || !ctx || !ctx->mrec || !ctx->attr) {
59 printf(stderr, "attr_find() received NULL pointer!\n");
65 * Iterate over attributes in mft record starting at @ctx->attr.
66 * Note: Not using while/do/for loops so the comparison code
67 * does not get indented out of the 80 characters wide screen... (AIA)
71 a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
72 if (a < ctx->mrec || a > (char*)ctx->mrec + vol->mft_record_size)
76 /* We catch $END with this more general check, too... */
77 if (le32_to_cpu(a->type) > le32_to_cpu(type))
83 /* If no @name is specified, check for @val. */
86 /* If no @val specified, we are done. */
91 rv = memcmp(val, (char*)a + le16_to_cpu(a->value_offset),
92 min(val_len, le32_to_cpu(a->value_length)));
93 /* If @val collates after the current attribute's value,
94 continue searching as a matching attribute might follow. */
96 register u32 avl = le32_to_cpu(a->value_length);
105 if (ntfs_names_are_equal(name, name_len,
106 (wchar_t*)((char*)a + le16_to_cpu(a->name_offset)),
107 a->name_length, ic, vol->upcase, vol->upcase_len))
109 { register int rc = ntfs_names_collate(vol->upcase,
110 vol->upcase_len, name, name_len,
111 (wchar_t*)((char*)a + le16_to_cpu(a->name_offset)),
112 a->name_length, IGNORE_CASE, 1);
113 /* If case insensitive collation of names collates @name
114 before a->name, there is no matching attribute. */
117 /* If the strings are not equal, continue search. */
121 /* If case sensitive collation of names doesn't collate @name before
122 a->name, we continue the search. Otherwise we haven't found it. */
123 if (ntfs_names_collate(vol->upcase, vol->upcase_len, name, name_len,
124 (wchar_t*)((char*)a + le16_to_cpu(a->name_offset)),
125 a->name_length, CASE_SENSITIVE, 1) != -1)
131 printf(stderr, "find_attr(): File is corrupt. Run chkdsk.\n");
136 BOOL external_attr_lookup(const ntfs_volume *vol, const MFT_REFERENCE mref,
137 const ATTR_TYPES type, const wchar_t *name,
138 const u32 name_len, const IGNORE_CASE_BOOL ic,
139 const s64 lowest_vcn, const u8 *val,
140 const u32 val_len, ntfs_attr_search_ctx *ctx)
142 ATTR_LIST_ENTRY *al_pos, **al_val, *al_val_start, *al_next_pos;
143 ATTR_RECORD *attr_pos;
144 MFT_RECORD *mrec, *m;
152 al_val = &ctx->alist_val;
153 if (ctx->alist_val_end <= *al_val && !ctx->is_first)
158 goto already_have_the_base_and_is_first;
159 al_val_start = *al_val;
160 al_pos = (char*)*al_val + le16_to_cpu((*al_val)->length);
164 if (al_pos < ctx->alist_val_end)
165 goto al_pos_below_alist_val_end;
170 if (!type || var1 || type == al_pos->type)
172 if (le32_to_cpu(al_pos->type) > le32_to_cpu(type))
174 al_pos = al_next_pos;
176 already_have_the_base_and_is_first:
177 ctx->is_first = FALSE;
178 if (*al_val < ctx->alist_val_end)
181 // FIXME: CcUnpinData(ctx->base);
186 if (ntfs_file_record_read(vol, mref, &ctx->mrec, &ctx->attr) < 0)
188 ctx->base = ctx->mrec;
189 attr_find(vol, type, name, name_len, ic, val, val_len, ctx);
191 al_pos_below_alist_val_end:
192 if (al_pos < ctx->alist_val)
194 if (al_pos >= ctx->alist_val_end)
198 al_next_pos = (ATTR_LIST_ENTRY*)((char*)al_pos +
199 le16_to_cpu(al_pos->length));
204 al_name_len = al_pos->name_length;
205 al_name = (wchar_t*)((char*)al_pos + al_pos->name_offset);
207 goto compare_lowest_vcn;
208 if (ic == CASE_SENSITIVE) {
209 if (name_len == al_name_len &&
210 !memcmp(al_name, name, al_name_len << 1))
214 } else /* IGNORE_CASE */
215 rc = ntfs_names_are_equal(al_name, al_name_len, name, name_len,
216 ic, vol->upcase, vol->upcase_len);
218 goto compare_lowest_vcn;
219 rc = ntfs_names_collate(vol->upcase, vol->upcase_len, name, name_len,
220 al_name, al_name_len, IGNORE_CASE, 1);
222 goto name_collates_before_al_name;
223 if (!rc && ntfs_names_collate(vol->upcase, vol->upcase_len, name,
224 name_len, al_name, al_name_len,
225 IGNORE_CASE, 0) == -1)
226 goto name_collates_before_al_name;
227 al_pos = al_next_pos;
229 name_collates_before_al_name:
232 if (lowest_vcn && !var1 && al_next_pos < ctx->alist_val_end &&
233 sle64_to_cpu(al_next_pos->lowest_vcn) <= sle64_to_cpu(lowest_vcn) &&
234 al_next_pos->type == al_pos->type &&
235 al_next_pos->name_length == al_name_len &&
236 !memcmp((char*)al_next_pos + al_next_pos->name_offset, al_name,
238 al_pos = al_next_pos;
241 /* Don't mask the sequence number. If it isn't equal, the ref is stale.
244 al_pos->mft_reference == al_val_start->mft_reference) {
246 attr_pos = (ATTR_RECORD*)((char*)mrec +
247 le16_to_cpu(mrec->attrs_offset));
250 // FIXME: CcUnpinData(ctx->base);
253 if (ntfs_file_record_read(vol,
254 le64_to_cpu(al_pos->mft_reference),
258 ctx->base = ctx->mrec = m;
261 do_next_attr_loop_start:
262 if (attr_pos < mrec || attr_pos > (char*)mrec + vol->mft_record_size)
264 if (attr_pos->type == AT_END)
265 goto do_next_al_entry;
266 if (!attr_pos->length)
268 if (al_pos->instance != attr_pos->instance)
270 if (al_pos->type != attr_pos->type)
271 goto do_next_al_entry;
273 goto skip_name_comparison;
274 if (attr_pos->name_length != al_name_len)
275 goto do_next_al_entry;
276 if (memcmp((wchar_t*)((char*)attr_pos +
277 le16_to_cpu(attr_pos->name_offset)), al_name,
278 attr_pos->name_length << 1))
279 goto do_next_al_entry;
280 skip_name_comparison:
282 ctx->attr = attr_pos;
287 if (attr_pos->non_resident)
289 if (le32_to_cpu(attr_pos->value_length) != val_len)
291 if (!memcmp((char*)attr_pos + le16_to_cpu(attr_pos->value_offset),
295 attr_pos = (ATTR_RECORD*)((char*)attr_pos +
296 le32_to_cpu(attr_pos->length));
297 goto do_next_attr_loop_start;
301 al_pos = (ATTR_RECORD*)((char*)al_pos + le16_to_cpu(al_pos->length));
305 *al_val = (ATTR_RECORD*)((char*)al_pos +
306 le16_to_cpu(al_pos->length));
308 // FIXME: CcUnpinData(ctx->base);
313 if (ntfs_file_record_read(vol, mref, &mrec, &ctx->attr) < 0)
316 attr_find(vol, type, name, name_len, ic, val, val_len, ctx);
320 fprintf(stderr, "lookup_attr() encountered corrupt file record.\n");
325 BOOL attr_lookup(const ntfs_volume *vol, const MFT_REFERENCE *mref,
326 const ATTR_TYPES type, const wchar_t *name,
327 const u32 name_len, const IGNORE_CASE_BOOL ic,
328 const s64 lowest_vcn, const u8 *val, const u32 val_len,
329 ntfs_attr_search_ctx *ctx)
337 printf(stderr, "lookup_attr() received NULL pointer!\n");
342 goto already_have_the_base;
343 if (ntfs_file_record_read(vol, mref, &m, &a) < 0)
345 ctx->base = ctx->mrec = m;
347 ctx->alist_mrec = ctx->alist_attr = ctx->alist_val = NULL;
349 * Look for an attribute list and at the same time check for attributes
350 * which collate before the attribute list (i.e. $STANDARD_INFORMATION).
352 if (le32_to_cpu(a->type) > le32_to_cpu(AT_ATTRIBUTE_LIST))
357 if (a->type == AT_ATTRIBUTE_LIST)
358 goto attr_list_present;
359 a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
360 if (a < m || a > (char*)m + vol->mft_record_size)
362 if (le32_to_cpu(a->type) <= le32_to_cpu(AT_ATTRIBUTE_LIST))
365 if (!type || type == AT_STANDARD_INFORMATION &&
366 a->type == AT_STANDARD_INFORMATION)
369 return attr_find(vol, type, name, name_len, ic, val, val_len, ctx);
373 already_have_the_base:
375 * If ctx->is_first, search starting with ctx->attr. Otherwise
376 * continue search after ctx->attr.
382 a = (ATTR_RECORD*)((char*)ctx->attr +
383 le32_to_cpu(ctx->attr->length));
384 if (a < m || a > (char*)m + vol->mft_record_size)
386 if (a->type == AT_END)
395 * Looking for zero means we return the first attribute, which will
396 * be the first one listed in the attribute list.
400 goto search_attr_list;
401 if (type == AT_ATTRIBUTE_LIST)
405 * "a" contains the attribute list attribute at this stage.
408 len = ntfs_get_attribute_value_length(a);
410 if (len > 0x40000LL) {
411 printf(stderr, "lookup_attr() found corrupt attribute list.\n");
415 ctx->alist_val_len = len;
416 if (!(ctx->alist_val = malloc(ctx->alist_val_len))) {
418 printf(stderr, "lookup_attr() failed to allocate memory for "
419 "attribute list value.\n");
423 if (ntfs_get_attribute_value(vol, ctx->mrec, a, ctx->alist_val) !=
424 ctx->alist_val_len) {
426 printf(stderr, "lookup_attr() failed to read attribute list "
431 ctx->alist_val_end = (char*)ctx->alist_val + ctx->alist_val_len;
432 if (a->non_resident) {
433 ctx->alist_old_base = ctx->alist_val_base;
434 ctx->alist_val_base = ctx->base;
436 } else if (ctx->base) {
437 // FIXME: CcUnpinData(ctx->base);
441 return external_attr_lookup(vol, mref, type, name, name_len, ic,
442 lowest_vcn, val, val_len, ctx);
445 fprintf(stderr, "attr_lookup() encountered corrupt file record.\n");