Release: captive1 -> captive2cvs
[ntfsprogs.git] / libntfs / attrib_RE.txt
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. */
5
6 /*
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
16  *
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
21  * TRUE.
22  *
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.
29  *
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)
34  */
35 typedef struct {
36         u8 *base;
37         MFT_RECORD *mrec;
38         ATTR_RECORD *attr;
39
40         u8 *alist_val_base;
41         MFT_RECORD *alist_mrec;
42         ATTR_RECORD *alist_attr;
43         ATTR_LIST_ENTRY *alist_val;
44         ATTR_LIST_ENTRY *alist_val_end;
45         u32 alist_val_len;
46         IS_FIRST_BOOL is_first;
47         u8 *alist_old_base;
48 } attr_search_context;
49
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)
54 {
55         ATTR_RECORD *a;
56
57 #ifdef DEBUG
58         if (!vol || !ctx || !ctx->mrec || !ctx->attr) {
59                 printf(stderr, "attr_find() received NULL pointer!\n");
60                 return FALSE;
61         }
62 #endif
63         a = ctx->attr;
64         /*
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)
68          */
69         goto search_loop;
70 do_next:
71         a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
72         if (a < ctx->mrec || a > (char*)ctx->mrec + vol->mft_record_size)
73                 goto file_corrupt;
74         ctx->attr = a;
75 search_loop:
76         /* We catch $END with this more general check, too... */
77         if (le32_to_cpu(a->type) > le32_to_cpu(type))
78                 goto not_found;
79         if (!a->length)
80                 goto file_corrupt;
81         if (a->type != type)
82                 goto do_next;
83         /* If no @name is specified, check for @val. */
84         if (!name) {
85                 register int rv;
86                 /* If no @val specified, we are done. */
87                 if (!val) {
88 found_it:
89                         return TRUE;
90                 }
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. */
95                 if (!rv) {
96                         register u32 avl = le32_to_cpu(a->value_length);
97                         if (val_len == avl)
98                                 goto found_it;
99                         if (val_len > avl)
100                                 goto do_next;
101                 } else if (rv > 0)
102                         goto do_next;
103                 goto not_found;
104         }
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))
108                 goto found_it;
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. */
115                 if (rc == -1)
116                         goto not_found;
117                 /* If the strings are not equal, continue search. */
118                 if (rc)
119                         goto do_next;
120         }
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)
126                 goto do_next;
127 not_found:
128         return FALSE;
129 file_corrupt:
130 #ifdef DEBUG
131         printf(stderr, "find_attr(): File is corrupt. Run chkdsk.\n");
132 #endif
133         goto not_found;
134 }
135
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)
141 {
142         ATTR_LIST_ENTRY *al_pos, **al_val, *al_val_start, *al_next_pos;
143         ATTR_RECORD *attr_pos;
144         MFT_RECORD *mrec, *m;
145         u8 var1 = 0;
146         u8 var2 = 0;
147         u8 var3;
148         int rc;
149         wchar_t *al_name;
150         u32 al_name_len;
151
152         al_val = &ctx->alist_val;
153         if (ctx->alist_val_end <= *al_val && !ctx->is_first)
154                 goto file_corrupt;
155         al_val_start = 0;
156         if (ctx->base) {
157                 if (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);
161         } else
162                 al_pos = *al_val;
163 do_next:
164         if (al_pos < ctx->alist_val_end)
165                 goto al_pos_below_alist_val_end;
166         var1 = var2 = 1;
167         al_pos = *al_val;
168 do_next_2:
169         *al_val = al_pos;
170         if (!type || var1 || type == al_pos->type)
171                 goto compare_names;
172         if (le32_to_cpu(al_pos->type) > le32_to_cpu(type))
173                 goto gone_too_far;
174         al_pos = al_next_pos;
175         goto do_next;
176 already_have_the_base_and_is_first:
177         ctx->is_first = FALSE;
178         if (*al_val < ctx->alist_val_end)
179                 goto do_next;
180         if (ctx->base) {
181                 // FIXME: CcUnpinData(ctx->base);
182                 ctx->base = NULL;
183         }
184         if (!type)
185                 return FALSE;
186         if (ntfs_file_record_read(vol, mref, &ctx->mrec, &ctx->attr) < 0)
187                 return FALSE;
188         ctx->base = ctx->mrec;
189         attr_find(vol, type, name, name_len, ic, val, val_len, ctx);
190         return FALSE;
191 al_pos_below_alist_val_end:
192         if (al_pos < ctx->alist_val)
193                 goto file_corrupt;
194         if (al_pos >= ctx->alist_val_end)
195                 goto file_corrupt;
196         if (!al_pos->length)
197                 goto file_corrupt;
198         al_next_pos = (ATTR_LIST_ENTRY*)((char*)al_pos +
199                                                 le16_to_cpu(al_pos->length));
200         goto do_next_2;
201 gone_too_far:
202         var1 = 1;
203 compare_names:
204         al_name_len = al_pos->name_length;
205         al_name = (wchar_t*)((char*)al_pos + al_pos->name_offset);
206         if (!name || var1)
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))
211                         rc = TRUE;
212                 else
213                         rc = FALSE;
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);
217         if (rc)
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);
221         if (rc == -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;
228         goto do_next;
229 name_collates_before_al_name:
230         var1 = 1;
231 compare_lowest_vcn:
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,
237                                                         al_name_len << 1)) {
238                 al_pos = al_next_pos;
239                 goto do_next;
240         }
241         /* Don't mask the sequence number. If it isn't equal, the ref is stale.
242          */
243         if (al_val_start &&
244                         al_pos->mft_reference == al_val_start->mft_reference) {
245                 mrec = ctx->mrec;
246                 attr_pos = (ATTR_RECORD*)((char*)mrec +
247                                         le16_to_cpu(mrec->attrs_offset));
248         } else {
249                 if (ctx->base) {
250                         // FIXME: CcUnpinData(ctx->base);
251                         ctx->base = 0;
252                 }
253                 if (ntfs_file_record_read(vol,
254                                 le64_to_cpu(al_pos->mft_reference),
255                                 &m, &attr_pos) < 0)
256                         return FALSE;
257                 mrec = ctx->mrec;
258                 ctx->base = ctx->mrec = m;
259         }
260         var3 = 0;
261 do_next_attr_loop_start:
262         if (attr_pos < mrec || attr_pos > (char*)mrec + vol->mft_record_size)
263                 goto file_corrupt;
264         if (attr_pos->type == AT_END)
265                 goto do_next_al_entry;
266         if (!attr_pos->length)
267                 goto file_corrupt;
268         if (al_pos->instance != attr_pos->instance)
269                 goto do_next_attr;
270         if (al_pos->type != attr_pos->type)
271                 goto do_next_al_entry;
272         if (!name)
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:
281         var3 = 1;
282         ctx->attr = attr_pos;
283         if (var1)
284                 goto loc_5217c;
285         if (!val)
286                 return TRUE;
287         if (attr_pos->non_resident)
288                 goto do_next_attr;
289         if (le32_to_cpu(attr_pos->value_length) != val_len)
290                 goto do_next_attr;
291         if (!memcmp((char*)attr_pos + le16_to_cpu(attr_pos->value_offset),
292                     val, val_len))
293                 return TRUE;
294 do_next_attr:
295         attr_pos = (ATTR_RECORD*)((char*)attr_pos +
296                                                 le32_to_cpu(attr_pos->length));
297         goto do_next_attr_loop_start;
298 do_next_al_entry:
299         if (!var3)
300                 goto file_corrupt;
301         al_pos = (ATTR_RECORD*)((char*)al_pos + le16_to_cpu(al_pos->length));
302         goto do_next;
303 loc_5217c:
304         if (var2)
305                 *al_val = (ATTR_RECORD*)((char*)al_pos +
306                                                 le16_to_cpu(al_pos->length));
307         if (ctx->base) {
308                 // FIXME: CcUnpinData(ctx->base);
309                 ctx->base = 0;
310         }
311         if (!type)
312                 return FALSE;
313         if (ntfs_file_record_read(vol, mref, &mrec, &ctx->attr) < 0)
314                 return FALSE;
315         ctx->base = mrec;
316         attr_find(vol, type, name, name_len, ic, val, val_len, ctx);
317         return FALSE;
318 file_corrupt:
319 #ifdef DEBUG
320         fprintf(stderr, "lookup_attr() encountered corrupt file record.\n");
321 #endif
322         return FALSE;
323 }
324
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)
330 {
331         MFT_RECORD *m;
332         ATTR_RECORD *a;
333         s64 len;
334
335         if (!vol || !ctx) {
336 #ifdef DEBUG
337                 printf(stderr, "lookup_attr() received NULL pointer!\n");
338 #endif
339                 return FALSE;
340         }
341         if (ctx->base)
342                 goto already_have_the_base;
343         if (ntfs_file_record_read(vol, mref, &m, &a) < 0)
344                 return FALSE;
345         ctx->base = ctx->mrec = m;
346         ctx->attr = a;
347         ctx->alist_mrec = ctx->alist_attr = ctx->alist_val = NULL;
348         /*
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).
351          */
352         if (le32_to_cpu(a->type) > le32_to_cpu(AT_ATTRIBUTE_LIST))
353                 goto no_attr_list;
354 do_next:
355         if (!a->length)
356                 goto file_corrupt;
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)
361                 goto file_corrupt;
362         if (le32_to_cpu(a->type) <= le32_to_cpu(AT_ATTRIBUTE_LIST))
363                 goto do_next;
364 no_attr_list:
365         if (!type || type == AT_STANDARD_INFORMATION &&
366                         a->type == AT_STANDARD_INFORMATION)
367                 goto found_it;
368 call_find_attr:
369         return attr_find(vol, type, name, name_len, ic, val, val_len, ctx);
370 found_it:
371         ctx->attr = a;
372         return TRUE;
373 already_have_the_base:
374         /*
375          * If ctx->is_first, search starting with ctx->attr. Otherwise
376          * continue search after ctx->attr.
377          */
378         if (ctx->is_first) {
379                 a = ctx->attr;
380                 ctx->is_first = 0;
381         } else
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)
385                 goto file_corrupt;
386         if (a->type == AT_END)
387                 return FALSE;
388         if (!a->length)
389                 goto file_corrupt;
390         if (type)
391                 goto call_find_attr;
392         goto found_it;
393 attr_list_present:
394         /*
395          * Looking for zero means we return the first attribute, which will
396          * be the first one listed in the attribute list.
397          */
398         ctx->attr = a;
399         if (!type)
400                 goto search_attr_list;
401         if (type == AT_ATTRIBUTE_LIST)
402                 return TRUE;
403 search_attr_list:
404         /*
405          * "a" contains the attribute list attribute at this stage.
406          */
407         ctx->alist_attr = a;
408         len = ntfs_get_attribute_value_length(a);
409 #ifdef DEBUG
410         if (len > 0x40000LL) {
411                 printf(stderr, "lookup_attr() found corrupt attribute list.\n");
412                 return FALSE;
413         }
414 #endif
415         ctx->alist_val_len = len;
416         if (!(ctx->alist_val = malloc(ctx->alist_val_len))) {
417 #ifdef DEBUG
418                 printf(stderr, "lookup_attr() failed to allocate memory for "
419                                 "attribute list value.\n");
420 #endif
421                 return FALSE;
422         }
423         if (ntfs_get_attribute_value(vol, ctx->mrec, a, ctx->alist_val) !=
424             ctx->alist_val_len) {
425 #ifdef DEBUG
426                 printf(stderr, "lookup_attr() failed to read attribute list "
427                                 "value.\n");
428 #endif
429                 return FALSE;
430         }
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;
435                 ctx->base = NULL;
436         } else if (ctx->base) {
437                 // FIXME: CcUnpinData(ctx->base);
438                 ctx->base = NULL;
439         }
440 lookup_external:
441         return external_attr_lookup(vol, mref, type, name, name_len, ic,
442                                     lowest_vcn, val, val_len, ctx);
443 file_corrupt:
444 #ifdef DEBUG
445         fprintf(stderr, "attr_lookup() encountered corrupt file record.\n");
446 #endif
447         return FALSE;
448 }
449