diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/ntfs/attrib.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/ntfs/attrib.c')
-rw-r--r-- | fs/ntfs/attrib.c | 1258 |
1 files changed, 1258 insertions, 0 deletions
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c new file mode 100644 index 000000000000..1ff7f90a18b0 --- /dev/null +++ b/fs/ntfs/attrib.c | |||
@@ -0,0 +1,1258 @@ | |||
1 | /** | ||
2 | * attrib.c - NTFS attribute operations. Part of the Linux-NTFS project. | ||
3 | * | ||
4 | * Copyright (c) 2001-2004 Anton Altaparmakov | ||
5 | * Copyright (c) 2002 Richard Russon | ||
6 | * | ||
7 | * This program/include file is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as published | ||
9 | * by the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program/include file is distributed in the hope that it will be | ||
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty | ||
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program (in the main directory of the Linux-NTFS | ||
19 | * distribution in the file COPYING); if not, write to the Free Software | ||
20 | * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <linux/buffer_head.h> | ||
24 | |||
25 | #include "attrib.h" | ||
26 | #include "debug.h" | ||
27 | #include "layout.h" | ||
28 | #include "mft.h" | ||
29 | #include "ntfs.h" | ||
30 | #include "types.h" | ||
31 | |||
32 | /** | ||
33 | * ntfs_map_runlist - map (a part of) a runlist of an ntfs inode | ||
34 | * @ni: ntfs inode for which to map (part of) a runlist | ||
35 | * @vcn: map runlist part containing this vcn | ||
36 | * | ||
37 | * Map the part of a runlist containing the @vcn of the ntfs inode @ni. | ||
38 | * | ||
39 | * Return 0 on success and -errno on error. | ||
40 | * | ||
41 | * Locking: - The runlist must be unlocked on entry and is unlocked on return. | ||
42 | * - This function takes the lock for writing and modifies the runlist. | ||
43 | */ | ||
44 | int ntfs_map_runlist(ntfs_inode *ni, VCN vcn) | ||
45 | { | ||
46 | ntfs_inode *base_ni; | ||
47 | ntfs_attr_search_ctx *ctx; | ||
48 | MFT_RECORD *mrec; | ||
49 | int err = 0; | ||
50 | |||
51 | ntfs_debug("Mapping runlist part containing vcn 0x%llx.", | ||
52 | (unsigned long long)vcn); | ||
53 | |||
54 | if (!NInoAttr(ni)) | ||
55 | base_ni = ni; | ||
56 | else | ||
57 | base_ni = ni->ext.base_ntfs_ino; | ||
58 | |||
59 | mrec = map_mft_record(base_ni); | ||
60 | if (IS_ERR(mrec)) | ||
61 | return PTR_ERR(mrec); | ||
62 | ctx = ntfs_attr_get_search_ctx(base_ni, mrec); | ||
63 | if (unlikely(!ctx)) { | ||
64 | err = -ENOMEM; | ||
65 | goto err_out; | ||
66 | } | ||
67 | err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, | ||
68 | CASE_SENSITIVE, vcn, NULL, 0, ctx); | ||
69 | if (unlikely(err)) | ||
70 | goto put_err_out; | ||
71 | |||
72 | down_write(&ni->runlist.lock); | ||
73 | /* Make sure someone else didn't do the work while we were sleeping. */ | ||
74 | if (likely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) <= | ||
75 | LCN_RL_NOT_MAPPED)) { | ||
76 | runlist_element *rl; | ||
77 | |||
78 | rl = ntfs_mapping_pairs_decompress(ni->vol, ctx->attr, | ||
79 | ni->runlist.rl); | ||
80 | if (IS_ERR(rl)) | ||
81 | err = PTR_ERR(rl); | ||
82 | else | ||
83 | ni->runlist.rl = rl; | ||
84 | } | ||
85 | up_write(&ni->runlist.lock); | ||
86 | |||
87 | put_err_out: | ||
88 | ntfs_attr_put_search_ctx(ctx); | ||
89 | err_out: | ||
90 | unmap_mft_record(base_ni); | ||
91 | return err; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * ntfs_find_vcn - find a vcn in the runlist described by an ntfs inode | ||
96 | * @ni: ntfs inode describing the runlist to search | ||
97 | * @vcn: vcn to find | ||
98 | * @need_write: if false, lock for reading and if true, lock for writing | ||
99 | * | ||
100 | * Find the virtual cluster number @vcn in the runlist described by the ntfs | ||
101 | * inode @ni and return the address of the runlist element containing the @vcn. | ||
102 | * The runlist is left locked and the caller has to unlock it. If @need_write | ||
103 | * is true, the runlist is locked for writing and if @need_write is false, the | ||
104 | * runlist is locked for reading. In the error case, the runlist is not left | ||
105 | * locked. | ||
106 | * | ||
107 | * Note you need to distinguish between the lcn of the returned runlist element | ||
108 | * being >= 0 and LCN_HOLE. In the later case you have to return zeroes on | ||
109 | * read and allocate clusters on write. | ||
110 | * | ||
111 | * Return the runlist element containing the @vcn on success and | ||
112 | * ERR_PTR(-errno) on error. You need to test the return value with IS_ERR() | ||
113 | * to decide if the return is success or failure and PTR_ERR() to get to the | ||
114 | * error code if IS_ERR() is true. | ||
115 | * | ||
116 | * The possible error return codes are: | ||
117 | * -ENOENT - No such vcn in the runlist, i.e. @vcn is out of bounds. | ||
118 | * -ENOMEM - Not enough memory to map runlist. | ||
119 | * -EIO - Critical error (runlist/file is corrupt, i/o error, etc). | ||
120 | * | ||
121 | * Locking: - The runlist must be unlocked on entry. | ||
122 | * - On failing return, the runlist is unlocked. | ||
123 | * - On successful return, the runlist is locked. If @need_write us | ||
124 | * true, it is locked for writing. Otherwise is is locked for | ||
125 | * reading. | ||
126 | */ | ||
127 | runlist_element *ntfs_find_vcn(ntfs_inode *ni, const VCN vcn, | ||
128 | const BOOL need_write) | ||
129 | { | ||
130 | runlist_element *rl; | ||
131 | int err = 0; | ||
132 | BOOL is_retry = FALSE; | ||
133 | |||
134 | ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, lock for %sing.", | ||
135 | ni->mft_no, (unsigned long long)vcn, | ||
136 | !need_write ? "read" : "writ"); | ||
137 | BUG_ON(!ni); | ||
138 | BUG_ON(!NInoNonResident(ni)); | ||
139 | BUG_ON(vcn < 0); | ||
140 | lock_retry_remap: | ||
141 | if (!need_write) | ||
142 | down_read(&ni->runlist.lock); | ||
143 | else | ||
144 | down_write(&ni->runlist.lock); | ||
145 | rl = ni->runlist.rl; | ||
146 | if (likely(rl && vcn >= rl[0].vcn)) { | ||
147 | while (likely(rl->length)) { | ||
148 | if (likely(vcn < rl[1].vcn)) { | ||
149 | if (likely(rl->lcn >= LCN_HOLE)) { | ||
150 | ntfs_debug("Done."); | ||
151 | return rl; | ||
152 | } | ||
153 | break; | ||
154 | } | ||
155 | rl++; | ||
156 | } | ||
157 | if (likely(rl->lcn != LCN_RL_NOT_MAPPED)) { | ||
158 | if (likely(rl->lcn == LCN_ENOENT)) | ||
159 | err = -ENOENT; | ||
160 | else | ||
161 | err = -EIO; | ||
162 | } | ||
163 | } | ||
164 | if (!need_write) | ||
165 | up_read(&ni->runlist.lock); | ||
166 | else | ||
167 | up_write(&ni->runlist.lock); | ||
168 | if (!err && !is_retry) { | ||
169 | /* | ||
170 | * The @vcn is in an unmapped region, map the runlist and | ||
171 | * retry. | ||
172 | */ | ||
173 | err = ntfs_map_runlist(ni, vcn); | ||
174 | if (likely(!err)) { | ||
175 | is_retry = TRUE; | ||
176 | goto lock_retry_remap; | ||
177 | } | ||
178 | /* | ||
179 | * -EINVAL and -ENOENT coming from a failed mapping attempt are | ||
180 | * equivalent to i/o errors for us as they should not happen in | ||
181 | * our code paths. | ||
182 | */ | ||
183 | if (err == -EINVAL || err == -ENOENT) | ||
184 | err = -EIO; | ||
185 | } else if (!err) | ||
186 | err = -EIO; | ||
187 | ntfs_error(ni->vol->sb, "Failed with error code %i.", err); | ||
188 | return ERR_PTR(err); | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * ntfs_attr_find - find (next) attribute in mft record | ||
193 | * @type: attribute type to find | ||
194 | * @name: attribute name to find (optional, i.e. NULL means don't care) | ||
195 | * @name_len: attribute name length (only needed if @name present) | ||
196 | * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) | ||
197 | * @val: attribute value to find (optional, resident attributes only) | ||
198 | * @val_len: attribute value length | ||
199 | * @ctx: search context with mft record and attribute to search from | ||
200 | * | ||
201 | * You should not need to call this function directly. Use ntfs_attr_lookup() | ||
202 | * instead. | ||
203 | * | ||
204 | * ntfs_attr_find() takes a search context @ctx as parameter and searches the | ||
205 | * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an | ||
206 | * attribute of @type, optionally @name and @val. | ||
207 | * | ||
208 | * If the attribute is found, ntfs_attr_find() returns 0 and @ctx->attr will | ||
209 | * point to the found attribute. | ||
210 | * | ||
211 | * If the attribute is not found, ntfs_attr_find() returns -ENOENT and | ||
212 | * @ctx->attr will point to the attribute before which the attribute being | ||
213 | * searched for would need to be inserted if such an action were to be desired. | ||
214 | * | ||
215 | * On actual error, ntfs_attr_find() returns -EIO. In this case @ctx->attr is | ||
216 | * undefined and in particular do not rely on it not changing. | ||
217 | * | ||
218 | * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it | ||
219 | * is FALSE, the search begins after @ctx->attr. | ||
220 | * | ||
221 | * If @ic is IGNORE_CASE, the @name comparisson is not case sensitive and | ||
222 | * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record | ||
223 | * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at | ||
224 | * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case | ||
225 | * sensitive. When @name is present, @name_len is the @name length in Unicode | ||
226 | * characters. | ||
227 | * | ||
228 | * If @name is not present (NULL), we assume that the unnamed attribute is | ||
229 | * being searched for. | ||
230 | * | ||
231 | * Finally, the resident attribute value @val is looked for, if present. If | ||
232 | * @val is not present (NULL), @val_len is ignored. | ||
233 | * | ||
234 | * ntfs_attr_find() only searches the specified mft record and it ignores the | ||
235 | * presence of an attribute list attribute (unless it is the one being searched | ||
236 | * for, obviously). If you need to take attribute lists into consideration, | ||
237 | * use ntfs_attr_lookup() instead (see below). This also means that you cannot | ||
238 | * use ntfs_attr_find() to search for extent records of non-resident | ||
239 | * attributes, as extents with lowest_vcn != 0 are usually described by the | ||
240 | * attribute list attribute only. - Note that it is possible that the first | ||
241 | * extent is only in the attribute list while the last extent is in the base | ||
242 | * mft record, so do not rely on being able to find the first extent in the | ||
243 | * base mft record. | ||
244 | * | ||
245 | * Warning: Never use @val when looking for attribute types which can be | ||
246 | * non-resident as this most likely will result in a crash! | ||
247 | */ | ||
248 | static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name, | ||
249 | const u32 name_len, const IGNORE_CASE_BOOL ic, | ||
250 | const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) | ||
251 | { | ||
252 | ATTR_RECORD *a; | ||
253 | ntfs_volume *vol = ctx->ntfs_ino->vol; | ||
254 | ntfschar *upcase = vol->upcase; | ||
255 | u32 upcase_len = vol->upcase_len; | ||
256 | |||
257 | /* | ||
258 | * Iterate over attributes in mft record starting at @ctx->attr, or the | ||
259 | * attribute following that, if @ctx->is_first is TRUE. | ||
260 | */ | ||
261 | if (ctx->is_first) { | ||
262 | a = ctx->attr; | ||
263 | ctx->is_first = FALSE; | ||
264 | } else | ||
265 | a = (ATTR_RECORD*)((u8*)ctx->attr + | ||
266 | le32_to_cpu(ctx->attr->length)); | ||
267 | for (;; a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) { | ||
268 | if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec + | ||
269 | le32_to_cpu(ctx->mrec->bytes_allocated)) | ||
270 | break; | ||
271 | ctx->attr = a; | ||
272 | if (unlikely(le32_to_cpu(a->type) > le32_to_cpu(type) || | ||
273 | a->type == AT_END)) | ||
274 | return -ENOENT; | ||
275 | if (unlikely(!a->length)) | ||
276 | break; | ||
277 | if (a->type != type) | ||
278 | continue; | ||
279 | /* | ||
280 | * If @name is present, compare the two names. If @name is | ||
281 | * missing, assume we want an unnamed attribute. | ||
282 | */ | ||
283 | if (!name) { | ||
284 | /* The search failed if the found attribute is named. */ | ||
285 | if (a->name_length) | ||
286 | return -ENOENT; | ||
287 | } else if (!ntfs_are_names_equal(name, name_len, | ||
288 | (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), | ||
289 | a->name_length, ic, upcase, upcase_len)) { | ||
290 | register int rc; | ||
291 | |||
292 | rc = ntfs_collate_names(name, name_len, | ||
293 | (ntfschar*)((u8*)a + | ||
294 | le16_to_cpu(a->name_offset)), | ||
295 | a->name_length, 1, IGNORE_CASE, | ||
296 | upcase, upcase_len); | ||
297 | /* | ||
298 | * If @name collates before a->name, there is no | ||
299 | * matching attribute. | ||
300 | */ | ||
301 | if (rc == -1) | ||
302 | return -ENOENT; | ||
303 | /* If the strings are not equal, continue search. */ | ||
304 | if (rc) | ||
305 | continue; | ||
306 | rc = ntfs_collate_names(name, name_len, | ||
307 | (ntfschar*)((u8*)a + | ||
308 | le16_to_cpu(a->name_offset)), | ||
309 | a->name_length, 1, CASE_SENSITIVE, | ||
310 | upcase, upcase_len); | ||
311 | if (rc == -1) | ||
312 | return -ENOENT; | ||
313 | if (rc) | ||
314 | continue; | ||
315 | } | ||
316 | /* | ||
317 | * The names match or @name not present and attribute is | ||
318 | * unnamed. If no @val specified, we have found the attribute | ||
319 | * and are done. | ||
320 | */ | ||
321 | if (!val) | ||
322 | return 0; | ||
323 | /* @val is present; compare values. */ | ||
324 | else { | ||
325 | register int rc; | ||
326 | |||
327 | rc = memcmp(val, (u8*)a + le16_to_cpu( | ||
328 | a->data.resident.value_offset), | ||
329 | min_t(u32, val_len, le32_to_cpu( | ||
330 | a->data.resident.value_length))); | ||
331 | /* | ||
332 | * If @val collates before the current attribute's | ||
333 | * value, there is no matching attribute. | ||
334 | */ | ||
335 | if (!rc) { | ||
336 | register u32 avl; | ||
337 | |||
338 | avl = le32_to_cpu( | ||
339 | a->data.resident.value_length); | ||
340 | if (val_len == avl) | ||
341 | return 0; | ||
342 | if (val_len < avl) | ||
343 | return -ENOENT; | ||
344 | } else if (rc < 0) | ||
345 | return -ENOENT; | ||
346 | } | ||
347 | } | ||
348 | ntfs_error(vol->sb, "Inode is corrupt. Run chkdsk."); | ||
349 | NVolSetErrors(vol); | ||
350 | return -EIO; | ||
351 | } | ||
352 | |||
353 | /** | ||
354 | * load_attribute_list - load an attribute list into memory | ||
355 | * @vol: ntfs volume from which to read | ||
356 | * @runlist: runlist of the attribute list | ||
357 | * @al_start: destination buffer | ||
358 | * @size: size of the destination buffer in bytes | ||
359 | * @initialized_size: initialized size of the attribute list | ||
360 | * | ||
361 | * Walk the runlist @runlist and load all clusters from it copying them into | ||
362 | * the linear buffer @al. The maximum number of bytes copied to @al is @size | ||
363 | * bytes. Note, @size does not need to be a multiple of the cluster size. If | ||
364 | * @initialized_size is less than @size, the region in @al between | ||
365 | * @initialized_size and @size will be zeroed and not read from disk. | ||
366 | * | ||
367 | * Return 0 on success or -errno on error. | ||
368 | */ | ||
369 | int load_attribute_list(ntfs_volume *vol, runlist *runlist, u8 *al_start, | ||
370 | const s64 size, const s64 initialized_size) | ||
371 | { | ||
372 | LCN lcn; | ||
373 | u8 *al = al_start; | ||
374 | u8 *al_end = al + initialized_size; | ||
375 | runlist_element *rl; | ||
376 | struct buffer_head *bh; | ||
377 | struct super_block *sb; | ||
378 | unsigned long block_size; | ||
379 | unsigned long block, max_block; | ||
380 | int err = 0; | ||
381 | unsigned char block_size_bits; | ||
382 | |||
383 | ntfs_debug("Entering."); | ||
384 | if (!vol || !runlist || !al || size <= 0 || initialized_size < 0 || | ||
385 | initialized_size > size) | ||
386 | return -EINVAL; | ||
387 | if (!initialized_size) { | ||
388 | memset(al, 0, size); | ||
389 | return 0; | ||
390 | } | ||
391 | sb = vol->sb; | ||
392 | block_size = sb->s_blocksize; | ||
393 | block_size_bits = sb->s_blocksize_bits; | ||
394 | down_read(&runlist->lock); | ||
395 | rl = runlist->rl; | ||
396 | /* Read all clusters specified by the runlist one run at a time. */ | ||
397 | while (rl->length) { | ||
398 | lcn = ntfs_rl_vcn_to_lcn(rl, rl->vcn); | ||
399 | ntfs_debug("Reading vcn = 0x%llx, lcn = 0x%llx.", | ||
400 | (unsigned long long)rl->vcn, | ||
401 | (unsigned long long)lcn); | ||
402 | /* The attribute list cannot be sparse. */ | ||
403 | if (lcn < 0) { | ||
404 | ntfs_error(sb, "ntfs_rl_vcn_to_lcn() failed. Cannot " | ||
405 | "read attribute list."); | ||
406 | goto err_out; | ||
407 | } | ||
408 | block = lcn << vol->cluster_size_bits >> block_size_bits; | ||
409 | /* Read the run from device in chunks of block_size bytes. */ | ||
410 | max_block = block + (rl->length << vol->cluster_size_bits >> | ||
411 | block_size_bits); | ||
412 | ntfs_debug("max_block = 0x%lx.", max_block); | ||
413 | do { | ||
414 | ntfs_debug("Reading block = 0x%lx.", block); | ||
415 | bh = sb_bread(sb, block); | ||
416 | if (!bh) { | ||
417 | ntfs_error(sb, "sb_bread() failed. Cannot " | ||
418 | "read attribute list."); | ||
419 | goto err_out; | ||
420 | } | ||
421 | if (al + block_size >= al_end) | ||
422 | goto do_final; | ||
423 | memcpy(al, bh->b_data, block_size); | ||
424 | brelse(bh); | ||
425 | al += block_size; | ||
426 | } while (++block < max_block); | ||
427 | rl++; | ||
428 | } | ||
429 | if (initialized_size < size) { | ||
430 | initialize: | ||
431 | memset(al_start + initialized_size, 0, size - initialized_size); | ||
432 | } | ||
433 | done: | ||
434 | up_read(&runlist->lock); | ||
435 | return err; | ||
436 | do_final: | ||
437 | if (al < al_end) { | ||
438 | /* | ||
439 | * Partial block. | ||
440 | * | ||
441 | * Note: The attribute list can be smaller than its allocation | ||
442 | * by multiple clusters. This has been encountered by at least | ||
443 | * two people running Windows XP, thus we cannot do any | ||
444 | * truncation sanity checking here. (AIA) | ||
445 | */ | ||
446 | memcpy(al, bh->b_data, al_end - al); | ||
447 | brelse(bh); | ||
448 | if (initialized_size < size) | ||
449 | goto initialize; | ||
450 | goto done; | ||
451 | } | ||
452 | brelse(bh); | ||
453 | /* Real overflow! */ | ||
454 | ntfs_error(sb, "Attribute list buffer overflow. Read attribute list " | ||
455 | "is truncated."); | ||
456 | err_out: | ||
457 | err = -EIO; | ||
458 | goto done; | ||
459 | } | ||
460 | |||
461 | /** | ||
462 | * ntfs_external_attr_find - find an attribute in the attribute list of an inode | ||
463 | * @type: attribute type to find | ||
464 | * @name: attribute name to find (optional, i.e. NULL means don't care) | ||
465 | * @name_len: attribute name length (only needed if @name present) | ||
466 | * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) | ||
467 | * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) | ||
468 | * @val: attribute value to find (optional, resident attributes only) | ||
469 | * @val_len: attribute value length | ||
470 | * @ctx: search context with mft record and attribute to search from | ||
471 | * | ||
472 | * You should not need to call this function directly. Use ntfs_attr_lookup() | ||
473 | * instead. | ||
474 | * | ||
475 | * Find an attribute by searching the attribute list for the corresponding | ||
476 | * attribute list entry. Having found the entry, map the mft record if the | ||
477 | * attribute is in a different mft record/inode, ntfs_attr_find() the attribute | ||
478 | * in there and return it. | ||
479 | * | ||
480 | * On first search @ctx->ntfs_ino must be the base mft record and @ctx must | ||
481 | * have been obtained from a call to ntfs_attr_get_search_ctx(). On subsequent | ||
482 | * calls @ctx->ntfs_ino can be any extent inode, too (@ctx->base_ntfs_ino is | ||
483 | * then the base inode). | ||
484 | * | ||
485 | * After finishing with the attribute/mft record you need to call | ||
486 | * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any | ||
487 | * mapped inodes, etc). | ||
488 | * | ||
489 | * If the attribute is found, ntfs_external_attr_find() returns 0 and | ||
490 | * @ctx->attr will point to the found attribute. @ctx->mrec will point to the | ||
491 | * mft record in which @ctx->attr is located and @ctx->al_entry will point to | ||
492 | * the attribute list entry for the attribute. | ||
493 | * | ||
494 | * If the attribute is not found, ntfs_external_attr_find() returns -ENOENT and | ||
495 | * @ctx->attr will point to the attribute in the base mft record before which | ||
496 | * the attribute being searched for would need to be inserted if such an action | ||
497 | * were to be desired. @ctx->mrec will point to the mft record in which | ||
498 | * @ctx->attr is located and @ctx->al_entry will point to the attribute list | ||
499 | * entry of the attribute before which the attribute being searched for would | ||
500 | * need to be inserted if such an action were to be desired. | ||
501 | * | ||
502 | * Thus to insert the not found attribute, one wants to add the attribute to | ||
503 | * @ctx->mrec (the base mft record) and if there is not enough space, the | ||
504 | * attribute should be placed in a newly allocated extent mft record. The | ||
505 | * attribute list entry for the inserted attribute should be inserted in the | ||
506 | * attribute list attribute at @ctx->al_entry. | ||
507 | * | ||
508 | * On actual error, ntfs_external_attr_find() returns -EIO. In this case | ||
509 | * @ctx->attr is undefined and in particular do not rely on it not changing. | ||
510 | */ | ||
511 | static int ntfs_external_attr_find(const ATTR_TYPE type, | ||
512 | const ntfschar *name, const u32 name_len, | ||
513 | const IGNORE_CASE_BOOL ic, const VCN lowest_vcn, | ||
514 | const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) | ||
515 | { | ||
516 | ntfs_inode *base_ni, *ni; | ||
517 | ntfs_volume *vol; | ||
518 | ATTR_LIST_ENTRY *al_entry, *next_al_entry; | ||
519 | u8 *al_start, *al_end; | ||
520 | ATTR_RECORD *a; | ||
521 | ntfschar *al_name; | ||
522 | u32 al_name_len; | ||
523 | int err = 0; | ||
524 | static const char *es = " Unmount and run chkdsk."; | ||
525 | |||
526 | ni = ctx->ntfs_ino; | ||
527 | base_ni = ctx->base_ntfs_ino; | ||
528 | ntfs_debug("Entering for inode 0x%lx, type 0x%x.", ni->mft_no, type); | ||
529 | if (!base_ni) { | ||
530 | /* First call happens with the base mft record. */ | ||
531 | base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; | ||
532 | ctx->base_mrec = ctx->mrec; | ||
533 | } | ||
534 | if (ni == base_ni) | ||
535 | ctx->base_attr = ctx->attr; | ||
536 | if (type == AT_END) | ||
537 | goto not_found; | ||
538 | vol = base_ni->vol; | ||
539 | al_start = base_ni->attr_list; | ||
540 | al_end = al_start + base_ni->attr_list_size; | ||
541 | if (!ctx->al_entry) | ||
542 | ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; | ||
543 | /* | ||
544 | * Iterate over entries in attribute list starting at @ctx->al_entry, | ||
545 | * or the entry following that, if @ctx->is_first is TRUE. | ||
546 | */ | ||
547 | if (ctx->is_first) { | ||
548 | al_entry = ctx->al_entry; | ||
549 | ctx->is_first = FALSE; | ||
550 | } else | ||
551 | al_entry = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + | ||
552 | le16_to_cpu(ctx->al_entry->length)); | ||
553 | for (;; al_entry = next_al_entry) { | ||
554 | /* Out of bounds check. */ | ||
555 | if ((u8*)al_entry < base_ni->attr_list || | ||
556 | (u8*)al_entry > al_end) | ||
557 | break; /* Inode is corrupt. */ | ||
558 | ctx->al_entry = al_entry; | ||
559 | /* Catch the end of the attribute list. */ | ||
560 | if ((u8*)al_entry == al_end) | ||
561 | goto not_found; | ||
562 | if (!al_entry->length) | ||
563 | break; | ||
564 | if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + | ||
565 | le16_to_cpu(al_entry->length) > al_end) | ||
566 | break; | ||
567 | next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + | ||
568 | le16_to_cpu(al_entry->length)); | ||
569 | if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) | ||
570 | goto not_found; | ||
571 | if (type != al_entry->type) | ||
572 | continue; | ||
573 | /* | ||
574 | * If @name is present, compare the two names. If @name is | ||
575 | * missing, assume we want an unnamed attribute. | ||
576 | */ | ||
577 | al_name_len = al_entry->name_length; | ||
578 | al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); | ||
579 | if (!name) { | ||
580 | if (al_name_len) | ||
581 | goto not_found; | ||
582 | } else if (!ntfs_are_names_equal(al_name, al_name_len, name, | ||
583 | name_len, ic, vol->upcase, vol->upcase_len)) { | ||
584 | register int rc; | ||
585 | |||
586 | rc = ntfs_collate_names(name, name_len, al_name, | ||
587 | al_name_len, 1, IGNORE_CASE, | ||
588 | vol->upcase, vol->upcase_len); | ||
589 | /* | ||
590 | * If @name collates before al_name, there is no | ||
591 | * matching attribute. | ||
592 | */ | ||
593 | if (rc == -1) | ||
594 | goto not_found; | ||
595 | /* If the strings are not equal, continue search. */ | ||
596 | if (rc) | ||
597 | continue; | ||
598 | /* | ||
599 | * FIXME: Reverse engineering showed 0, IGNORE_CASE but | ||
600 | * that is inconsistent with ntfs_attr_find(). The | ||
601 | * subsequent rc checks were also different. Perhaps I | ||
602 | * made a mistake in one of the two. Need to recheck | ||
603 | * which is correct or at least see what is going on... | ||
604 | * (AIA) | ||
605 | */ | ||
606 | rc = ntfs_collate_names(name, name_len, al_name, | ||
607 | al_name_len, 1, CASE_SENSITIVE, | ||
608 | vol->upcase, vol->upcase_len); | ||
609 | if (rc == -1) | ||
610 | goto not_found; | ||
611 | if (rc) | ||
612 | continue; | ||
613 | } | ||
614 | /* | ||
615 | * The names match or @name not present and attribute is | ||
616 | * unnamed. Now check @lowest_vcn. Continue search if the | ||
617 | * next attribute list entry still fits @lowest_vcn. Otherwise | ||
618 | * we have reached the right one or the search has failed. | ||
619 | */ | ||
620 | if (lowest_vcn && (u8*)next_al_entry >= al_start && | ||
621 | (u8*)next_al_entry + 6 < al_end && | ||
622 | (u8*)next_al_entry + le16_to_cpu( | ||
623 | next_al_entry->length) <= al_end && | ||
624 | sle64_to_cpu(next_al_entry->lowest_vcn) <= | ||
625 | lowest_vcn && | ||
626 | next_al_entry->type == al_entry->type && | ||
627 | next_al_entry->name_length == al_name_len && | ||
628 | ntfs_are_names_equal((ntfschar*)((u8*) | ||
629 | next_al_entry + | ||
630 | next_al_entry->name_offset), | ||
631 | next_al_entry->name_length, | ||
632 | al_name, al_name_len, CASE_SENSITIVE, | ||
633 | vol->upcase, vol->upcase_len)) | ||
634 | continue; | ||
635 | if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { | ||
636 | if (MSEQNO_LE(al_entry->mft_reference) != ni->seq_no) { | ||
637 | ntfs_error(vol->sb, "Found stale mft " | ||
638 | "reference in attribute list " | ||
639 | "of base inode 0x%lx.%s", | ||
640 | base_ni->mft_no, es); | ||
641 | err = -EIO; | ||
642 | break; | ||
643 | } | ||
644 | } else { /* Mft references do not match. */ | ||
645 | /* If there is a mapped record unmap it first. */ | ||
646 | if (ni != base_ni) | ||
647 | unmap_extent_mft_record(ni); | ||
648 | /* Do we want the base record back? */ | ||
649 | if (MREF_LE(al_entry->mft_reference) == | ||
650 | base_ni->mft_no) { | ||
651 | ni = ctx->ntfs_ino = base_ni; | ||
652 | ctx->mrec = ctx->base_mrec; | ||
653 | } else { | ||
654 | /* We want an extent record. */ | ||
655 | ctx->mrec = map_extent_mft_record(base_ni, | ||
656 | le64_to_cpu( | ||
657 | al_entry->mft_reference), &ni); | ||
658 | if (IS_ERR(ctx->mrec)) { | ||
659 | ntfs_error(vol->sb, "Failed to map " | ||
660 | "extent mft record " | ||
661 | "0x%lx of base inode " | ||
662 | "0x%lx.%s", | ||
663 | MREF_LE(al_entry-> | ||
664 | mft_reference), | ||
665 | base_ni->mft_no, es); | ||
666 | err = PTR_ERR(ctx->mrec); | ||
667 | if (err == -ENOENT) | ||
668 | err = -EIO; | ||
669 | /* Cause @ctx to be sanitized below. */ | ||
670 | ni = NULL; | ||
671 | break; | ||
672 | } | ||
673 | ctx->ntfs_ino = ni; | ||
674 | } | ||
675 | ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + | ||
676 | le16_to_cpu(ctx->mrec->attrs_offset)); | ||
677 | } | ||
678 | /* | ||
679 | * ctx->vfs_ino, ctx->mrec, and ctx->attr now point to the | ||
680 | * mft record containing the attribute represented by the | ||
681 | * current al_entry. | ||
682 | */ | ||
683 | /* | ||
684 | * We could call into ntfs_attr_find() to find the right | ||
685 | * attribute in this mft record but this would be less | ||
686 | * efficient and not quite accurate as ntfs_attr_find() ignores | ||
687 | * the attribute instance numbers for example which become | ||
688 | * important when one plays with attribute lists. Also, | ||
689 | * because a proper match has been found in the attribute list | ||
690 | * entry above, the comparison can now be optimized. So it is | ||
691 | * worth re-implementing a simplified ntfs_attr_find() here. | ||
692 | */ | ||
693 | a = ctx->attr; | ||
694 | /* | ||
695 | * Use a manual loop so we can still use break and continue | ||
696 | * with the same meanings as above. | ||
697 | */ | ||
698 | do_next_attr_loop: | ||
699 | if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec + | ||
700 | le32_to_cpu(ctx->mrec->bytes_allocated)) | ||
701 | break; | ||
702 | if (a->type == AT_END) | ||
703 | continue; | ||
704 | if (!a->length) | ||
705 | break; | ||
706 | if (al_entry->instance != a->instance) | ||
707 | goto do_next_attr; | ||
708 | /* | ||
709 | * If the type and/or the name are mismatched between the | ||
710 | * attribute list entry and the attribute record, there is | ||
711 | * corruption so we break and return error EIO. | ||
712 | */ | ||
713 | if (al_entry->type != a->type) | ||
714 | break; | ||
715 | if (!ntfs_are_names_equal((ntfschar*)((u8*)a + | ||
716 | le16_to_cpu(a->name_offset)), a->name_length, | ||
717 | al_name, al_name_len, CASE_SENSITIVE, | ||
718 | vol->upcase, vol->upcase_len)) | ||
719 | break; | ||
720 | ctx->attr = a; | ||
721 | /* | ||
722 | * If no @val specified or @val specified and it matches, we | ||
723 | * have found it! | ||
724 | */ | ||
725 | if (!val || (!a->non_resident && le32_to_cpu( | ||
726 | a->data.resident.value_length) == val_len && | ||
727 | !memcmp((u8*)a + | ||
728 | le16_to_cpu(a->data.resident.value_offset), | ||
729 | val, val_len))) { | ||
730 | ntfs_debug("Done, found."); | ||
731 | return 0; | ||
732 | } | ||
733 | do_next_attr: | ||
734 | /* Proceed to the next attribute in the current mft record. */ | ||
735 | a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length)); | ||
736 | goto do_next_attr_loop; | ||
737 | } | ||
738 | if (!err) { | ||
739 | ntfs_error(vol->sb, "Base inode 0x%lx contains corrupt " | ||
740 | "attribute list attribute.%s", base_ni->mft_no, | ||
741 | es); | ||
742 | err = -EIO; | ||
743 | } | ||
744 | if (ni != base_ni) { | ||
745 | if (ni) | ||
746 | unmap_extent_mft_record(ni); | ||
747 | ctx->ntfs_ino = base_ni; | ||
748 | ctx->mrec = ctx->base_mrec; | ||
749 | ctx->attr = ctx->base_attr; | ||
750 | } | ||
751 | if (err != -ENOMEM) | ||
752 | NVolSetErrors(vol); | ||
753 | return err; | ||
754 | not_found: | ||
755 | /* | ||
756 | * If we were looking for AT_END, we reset the search context @ctx and | ||
757 | * use ntfs_attr_find() to seek to the end of the base mft record. | ||
758 | */ | ||
759 | if (type == AT_END) { | ||
760 | ntfs_attr_reinit_search_ctx(ctx); | ||
761 | return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, | ||
762 | ctx); | ||
763 | } | ||
764 | /* | ||
765 | * The attribute was not found. Before we return, we want to ensure | ||
766 | * @ctx->mrec and @ctx->attr indicate the position at which the | ||
767 | * attribute should be inserted in the base mft record. Since we also | ||
768 | * want to preserve @ctx->al_entry we cannot reinitialize the search | ||
769 | * context using ntfs_attr_reinit_search_ctx() as this would set | ||
770 | * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see | ||
771 | * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve | ||
772 | * @ctx->al_entry as the remaining fields (base_*) are identical to | ||
773 | * their non base_ counterparts and we cannot set @ctx->base_attr | ||
774 | * correctly yet as we do not know what @ctx->attr will be set to by | ||
775 | * the call to ntfs_attr_find() below. | ||
776 | */ | ||
777 | if (ni != base_ni) | ||
778 | unmap_extent_mft_record(ni); | ||
779 | ctx->mrec = ctx->base_mrec; | ||
780 | ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + | ||
781 | le16_to_cpu(ctx->mrec->attrs_offset)); | ||
782 | ctx->is_first = TRUE; | ||
783 | ctx->ntfs_ino = base_ni; | ||
784 | ctx->base_ntfs_ino = NULL; | ||
785 | ctx->base_mrec = NULL; | ||
786 | ctx->base_attr = NULL; | ||
787 | /* | ||
788 | * In case there are multiple matches in the base mft record, need to | ||
789 | * keep enumerating until we get an attribute not found response (or | ||
790 | * another error), otherwise we would keep returning the same attribute | ||
791 | * over and over again and all programs using us for enumeration would | ||
792 | * lock up in a tight loop. | ||
793 | */ | ||
794 | do { | ||
795 | err = ntfs_attr_find(type, name, name_len, ic, val, val_len, | ||
796 | ctx); | ||
797 | } while (!err); | ||
798 | ntfs_debug("Done, not found."); | ||
799 | return err; | ||
800 | } | ||
801 | |||
802 | /** | ||
803 | * ntfs_attr_lookup - find an attribute in an ntfs inode | ||
804 | * @type: attribute type to find | ||
805 | * @name: attribute name to find (optional, i.e. NULL means don't care) | ||
806 | * @name_len: attribute name length (only needed if @name present) | ||
807 | * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) | ||
808 | * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) | ||
809 | * @val: attribute value to find (optional, resident attributes only) | ||
810 | * @val_len: attribute value length | ||
811 | * @ctx: search context with mft record and attribute to search from | ||
812 | * | ||
813 | * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must | ||
814 | * be the base mft record and @ctx must have been obtained from a call to | ||
815 | * ntfs_attr_get_search_ctx(). | ||
816 | * | ||
817 | * This function transparently handles attribute lists and @ctx is used to | ||
818 | * continue searches where they were left off at. | ||
819 | * | ||
820 | * After finishing with the attribute/mft record you need to call | ||
821 | * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any | ||
822 | * mapped inodes, etc). | ||
823 | * | ||
824 | * Return 0 if the search was successful and -errno if not. | ||
825 | * | ||
826 | * When 0, @ctx->attr is the found attribute and it is in mft record | ||
827 | * @ctx->mrec. If an attribute list attribute is present, @ctx->al_entry is | ||
828 | * the attribute list entry of the found attribute. | ||
829 | * | ||
830 | * When -ENOENT, @ctx->attr is the attribute which collates just after the | ||
831 | * attribute being searched for, i.e. if one wants to add the attribute to the | ||
832 | * mft record this is the correct place to insert it into. If an attribute | ||
833 | * list attribute is present, @ctx->al_entry is the attribute list entry which | ||
834 | * collates just after the attribute list entry of the attribute being searched | ||
835 | * for, i.e. if one wants to add the attribute to the mft record this is the | ||
836 | * correct place to insert its attribute list entry into. | ||
837 | * | ||
838 | * When -errno != -ENOENT, an error occured during the lookup. @ctx->attr is | ||
839 | * then undefined and in particular you should not rely on it not changing. | ||
840 | */ | ||
841 | int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name, | ||
842 | const u32 name_len, const IGNORE_CASE_BOOL ic, | ||
843 | const VCN lowest_vcn, const u8 *val, const u32 val_len, | ||
844 | ntfs_attr_search_ctx *ctx) | ||
845 | { | ||
846 | ntfs_inode *base_ni; | ||
847 | |||
848 | ntfs_debug("Entering."); | ||
849 | if (ctx->base_ntfs_ino) | ||
850 | base_ni = ctx->base_ntfs_ino; | ||
851 | else | ||
852 | base_ni = ctx->ntfs_ino; | ||
853 | /* Sanity check, just for debugging really. */ | ||
854 | BUG_ON(!base_ni); | ||
855 | if (!NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) | ||
856 | return ntfs_attr_find(type, name, name_len, ic, val, val_len, | ||
857 | ctx); | ||
858 | return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, | ||
859 | val, val_len, ctx); | ||
860 | } | ||
861 | |||
862 | /** | ||
863 | * ntfs_attr_init_search_ctx - initialize an attribute search context | ||
864 | * @ctx: attribute search context to initialize | ||
865 | * @ni: ntfs inode with which to initialize the search context | ||
866 | * @mrec: mft record with which to initialize the search context | ||
867 | * | ||
868 | * Initialize the attribute search context @ctx with @ni and @mrec. | ||
869 | */ | ||
870 | static inline void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, | ||
871 | ntfs_inode *ni, MFT_RECORD *mrec) | ||
872 | { | ||
873 | ctx->mrec = mrec; | ||
874 | /* Sanity checks are performed elsewhere. */ | ||
875 | ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); | ||
876 | ctx->is_first = TRUE; | ||
877 | ctx->ntfs_ino = ni; | ||
878 | ctx->al_entry = NULL; | ||
879 | ctx->base_ntfs_ino = NULL; | ||
880 | ctx->base_mrec = NULL; | ||
881 | ctx->base_attr = NULL; | ||
882 | } | ||
883 | |||
884 | /** | ||
885 | * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context | ||
886 | * @ctx: attribute search context to reinitialize | ||
887 | * | ||
888 | * Reinitialize the attribute search context @ctx, unmapping an associated | ||
889 | * extent mft record if present, and initialize the search context again. | ||
890 | * | ||
891 | * This is used when a search for a new attribute is being started to reset | ||
892 | * the search context to the beginning. | ||
893 | */ | ||
894 | void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx) | ||
895 | { | ||
896 | if (likely(!ctx->base_ntfs_ino)) { | ||
897 | /* No attribute list. */ | ||
898 | ctx->is_first = TRUE; | ||
899 | /* Sanity checks are performed elsewhere. */ | ||
900 | ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + | ||
901 | le16_to_cpu(ctx->mrec->attrs_offset)); | ||
902 | /* | ||
903 | * This needs resetting due to ntfs_external_attr_find() which | ||
904 | * can leave it set despite having zeroed ctx->base_ntfs_ino. | ||
905 | */ | ||
906 | ctx->al_entry = NULL; | ||
907 | return; | ||
908 | } /* Attribute list. */ | ||
909 | if (ctx->ntfs_ino != ctx->base_ntfs_ino) | ||
910 | unmap_extent_mft_record(ctx->ntfs_ino); | ||
911 | ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); | ||
912 | return; | ||
913 | } | ||
914 | |||
915 | /** | ||
916 | * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context | ||
917 | * @ni: ntfs inode with which to initialize the search context | ||
918 | * @mrec: mft record with which to initialize the search context | ||
919 | * | ||
920 | * Allocate a new attribute search context, initialize it with @ni and @mrec, | ||
921 | * and return it. Return NULL if allocation failed. | ||
922 | */ | ||
923 | ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) | ||
924 | { | ||
925 | ntfs_attr_search_ctx *ctx; | ||
926 | |||
927 | ctx = kmem_cache_alloc(ntfs_attr_ctx_cache, SLAB_NOFS); | ||
928 | if (ctx) | ||
929 | ntfs_attr_init_search_ctx(ctx, ni, mrec); | ||
930 | return ctx; | ||
931 | } | ||
932 | |||
933 | /** | ||
934 | * ntfs_attr_put_search_ctx - release an attribute search context | ||
935 | * @ctx: attribute search context to free | ||
936 | * | ||
937 | * Release the attribute search context @ctx, unmapping an associated extent | ||
938 | * mft record if present. | ||
939 | */ | ||
940 | void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) | ||
941 | { | ||
942 | if (ctx->base_ntfs_ino && ctx->ntfs_ino != ctx->base_ntfs_ino) | ||
943 | unmap_extent_mft_record(ctx->ntfs_ino); | ||
944 | kmem_cache_free(ntfs_attr_ctx_cache, ctx); | ||
945 | return; | ||
946 | } | ||
947 | |||
948 | /** | ||
949 | * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file | ||
950 | * @vol: ntfs volume to which the attribute belongs | ||
951 | * @type: attribute type which to find | ||
952 | * | ||
953 | * Search for the attribute definition record corresponding to the attribute | ||
954 | * @type in the $AttrDef system file. | ||
955 | * | ||
956 | * Return the attribute type definition record if found and NULL if not found. | ||
957 | */ | ||
958 | static ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, | ||
959 | const ATTR_TYPE type) | ||
960 | { | ||
961 | ATTR_DEF *ad; | ||
962 | |||
963 | BUG_ON(!vol->attrdef); | ||
964 | BUG_ON(!type); | ||
965 | for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < | ||
966 | vol->attrdef_size && ad->type; ++ad) { | ||
967 | /* We have not found it yet, carry on searching. */ | ||
968 | if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type))) | ||
969 | continue; | ||
970 | /* We found the attribute; return it. */ | ||
971 | if (likely(ad->type == type)) | ||
972 | return ad; | ||
973 | /* We have gone too far already. No point in continuing. */ | ||
974 | break; | ||
975 | } | ||
976 | /* Attribute not found. */ | ||
977 | ntfs_debug("Attribute type 0x%x not found in $AttrDef.", | ||
978 | le32_to_cpu(type)); | ||
979 | return NULL; | ||
980 | } | ||
981 | |||
982 | /** | ||
983 | * ntfs_attr_size_bounds_check - check a size of an attribute type for validity | ||
984 | * @vol: ntfs volume to which the attribute belongs | ||
985 | * @type: attribute type which to check | ||
986 | * @size: size which to check | ||
987 | * | ||
988 | * Check whether the @size in bytes is valid for an attribute of @type on the | ||
989 | * ntfs volume @vol. This information is obtained from $AttrDef system file. | ||
990 | * | ||
991 | * Return 0 if valid, -ERANGE if not valid, or -ENOENT if the attribute is not | ||
992 | * listed in $AttrDef. | ||
993 | */ | ||
994 | int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPE type, | ||
995 | const s64 size) | ||
996 | { | ||
997 | ATTR_DEF *ad; | ||
998 | |||
999 | BUG_ON(size < 0); | ||
1000 | /* | ||
1001 | * $ATTRIBUTE_LIST has a maximum size of 256kiB, but this is not | ||
1002 | * listed in $AttrDef. | ||
1003 | */ | ||
1004 | if (unlikely(type == AT_ATTRIBUTE_LIST && size > 256 * 1024)) | ||
1005 | return -ERANGE; | ||
1006 | /* Get the $AttrDef entry for the attribute @type. */ | ||
1007 | ad = ntfs_attr_find_in_attrdef(vol, type); | ||
1008 | if (unlikely(!ad)) | ||
1009 | return -ENOENT; | ||
1010 | /* Do the bounds check. */ | ||
1011 | if (((sle64_to_cpu(ad->min_size) > 0) && | ||
1012 | size < sle64_to_cpu(ad->min_size)) || | ||
1013 | ((sle64_to_cpu(ad->max_size) > 0) && size > | ||
1014 | sle64_to_cpu(ad->max_size))) | ||
1015 | return -ERANGE; | ||
1016 | return 0; | ||
1017 | } | ||
1018 | |||
1019 | /** | ||
1020 | * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident | ||
1021 | * @vol: ntfs volume to which the attribute belongs | ||
1022 | * @type: attribute type which to check | ||
1023 | * | ||
1024 | * Check whether the attribute of @type on the ntfs volume @vol is allowed to | ||
1025 | * be non-resident. This information is obtained from $AttrDef system file. | ||
1026 | * | ||
1027 | * Return 0 if the attribute is allowed to be non-resident, -EPERM if not, or | ||
1028 | * -ENOENT if the attribute is not listed in $AttrDef. | ||
1029 | */ | ||
1030 | int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type) | ||
1031 | { | ||
1032 | ATTR_DEF *ad; | ||
1033 | |||
1034 | /* | ||
1035 | * $DATA is always allowed to be non-resident even if $AttrDef does not | ||
1036 | * specify this in the flags of the $DATA attribute definition record. | ||
1037 | */ | ||
1038 | if (type == AT_DATA) | ||
1039 | return 0; | ||
1040 | /* Find the attribute definition record in $AttrDef. */ | ||
1041 | ad = ntfs_attr_find_in_attrdef(vol, type); | ||
1042 | if (unlikely(!ad)) | ||
1043 | return -ENOENT; | ||
1044 | /* Check the flags and return the result. */ | ||
1045 | if (ad->flags & CAN_BE_NON_RESIDENT) | ||
1046 | return 0; | ||
1047 | return -EPERM; | ||
1048 | } | ||
1049 | |||
1050 | /** | ||
1051 | * ntfs_attr_can_be_resident - check if an attribute can be resident | ||
1052 | * @vol: ntfs volume to which the attribute belongs | ||
1053 | * @type: attribute type which to check | ||
1054 | * | ||
1055 | * Check whether the attribute of @type on the ntfs volume @vol is allowed to | ||
1056 | * be resident. This information is derived from our ntfs knowledge and may | ||
1057 | * not be completely accurate, especially when user defined attributes are | ||
1058 | * present. Basically we allow everything to be resident except for index | ||
1059 | * allocation and $EA attributes. | ||
1060 | * | ||
1061 | * Return 0 if the attribute is allowed to be non-resident and -EPERM if not. | ||
1062 | * | ||
1063 | * Warning: In the system file $MFT the attribute $Bitmap must be non-resident | ||
1064 | * otherwise windows will not boot (blue screen of death)! We cannot | ||
1065 | * check for this here as we do not know which inode's $Bitmap is | ||
1066 | * being asked about so the caller needs to special case this. | ||
1067 | */ | ||
1068 | int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type) | ||
1069 | { | ||
1070 | if (type != AT_INDEX_ALLOCATION && type != AT_EA) | ||
1071 | return 0; | ||
1072 | return -EPERM; | ||
1073 | } | ||
1074 | |||
1075 | /** | ||
1076 | * ntfs_attr_record_resize - resize an attribute record | ||
1077 | * @m: mft record containing attribute record | ||
1078 | * @a: attribute record to resize | ||
1079 | * @new_size: new size in bytes to which to resize the attribute record @a | ||
1080 | * | ||
1081 | * Resize the attribute record @a, i.e. the resident part of the attribute, in | ||
1082 | * the mft record @m to @new_size bytes. | ||
1083 | * | ||
1084 | * Return 0 on success and -errno on error. The following error codes are | ||
1085 | * defined: | ||
1086 | * -ENOSPC - Not enough space in the mft record @m to perform the resize. | ||
1087 | * | ||
1088 | * Note: On error, no modifications have been performed whatsoever. | ||
1089 | * | ||
1090 | * Warning: If you make a record smaller without having copied all the data you | ||
1091 | * are interested in the data may be overwritten. | ||
1092 | */ | ||
1093 | int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) | ||
1094 | { | ||
1095 | ntfs_debug("Entering for new_size %u.", new_size); | ||
1096 | /* Align to 8 bytes if it is not already done. */ | ||
1097 | if (new_size & 7) | ||
1098 | new_size = (new_size + 7) & ~7; | ||
1099 | /* If the actual attribute length has changed, move things around. */ | ||
1100 | if (new_size != le32_to_cpu(a->length)) { | ||
1101 | u32 new_muse = le32_to_cpu(m->bytes_in_use) - | ||
1102 | le32_to_cpu(a->length) + new_size; | ||
1103 | /* Not enough space in this mft record. */ | ||
1104 | if (new_muse > le32_to_cpu(m->bytes_allocated)) | ||
1105 | return -ENOSPC; | ||
1106 | /* Move attributes following @a to their new location. */ | ||
1107 | memmove((u8*)a + new_size, (u8*)a + le32_to_cpu(a->length), | ||
1108 | le32_to_cpu(m->bytes_in_use) - ((u8*)a - | ||
1109 | (u8*)m) - le32_to_cpu(a->length)); | ||
1110 | /* Adjust @m to reflect the change in used space. */ | ||
1111 | m->bytes_in_use = cpu_to_le32(new_muse); | ||
1112 | /* Adjust @a to reflect the new size. */ | ||
1113 | if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length)) | ||
1114 | a->length = cpu_to_le32(new_size); | ||
1115 | } | ||
1116 | return 0; | ||
1117 | } | ||
1118 | |||
1119 | /** | ||
1120 | * ntfs_attr_set - fill (a part of) an attribute with a byte | ||
1121 | * @ni: ntfs inode describing the attribute to fill | ||
1122 | * @ofs: offset inside the attribute at which to start to fill | ||
1123 | * @cnt: number of bytes to fill | ||
1124 | * @val: the unsigned 8-bit value with which to fill the attribute | ||
1125 | * | ||
1126 | * Fill @cnt bytes of the attribute described by the ntfs inode @ni starting at | ||
1127 | * byte offset @ofs inside the attribute with the constant byte @val. | ||
1128 | * | ||
1129 | * This function is effectively like memset() applied to an ntfs attribute. | ||
1130 | * | ||
1131 | * Return 0 on success and -errno on error. An error code of -ESPIPE means | ||
1132 | * that @ofs + @cnt were outside the end of the attribute and no write was | ||
1133 | * performed. | ||
1134 | */ | ||
1135 | int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val) | ||
1136 | { | ||
1137 | ntfs_volume *vol = ni->vol; | ||
1138 | struct address_space *mapping; | ||
1139 | struct page *page; | ||
1140 | u8 *kaddr; | ||
1141 | pgoff_t idx, end; | ||
1142 | unsigned int start_ofs, end_ofs, size; | ||
1143 | |||
1144 | ntfs_debug("Entering for ofs 0x%llx, cnt 0x%llx, val 0x%hx.", | ||
1145 | (long long)ofs, (long long)cnt, val); | ||
1146 | BUG_ON(ofs < 0); | ||
1147 | BUG_ON(cnt < 0); | ||
1148 | if (!cnt) | ||
1149 | goto done; | ||
1150 | mapping = VFS_I(ni)->i_mapping; | ||
1151 | /* Work out the starting index and page offset. */ | ||
1152 | idx = ofs >> PAGE_CACHE_SHIFT; | ||
1153 | start_ofs = ofs & ~PAGE_CACHE_MASK; | ||
1154 | /* Work out the ending index and page offset. */ | ||
1155 | end = ofs + cnt; | ||
1156 | end_ofs = end & ~PAGE_CACHE_MASK; | ||
1157 | /* If the end is outside the inode size return -ESPIPE. */ | ||
1158 | if (unlikely(end > VFS_I(ni)->i_size)) { | ||
1159 | ntfs_error(vol->sb, "Request exceeds end of attribute."); | ||
1160 | return -ESPIPE; | ||
1161 | } | ||
1162 | end >>= PAGE_CACHE_SHIFT; | ||
1163 | /* If there is a first partial page, need to do it the slow way. */ | ||
1164 | if (start_ofs) { | ||
1165 | page = read_cache_page(mapping, idx, | ||
1166 | (filler_t*)mapping->a_ops->readpage, NULL); | ||
1167 | if (IS_ERR(page)) { | ||
1168 | ntfs_error(vol->sb, "Failed to read first partial " | ||
1169 | "page (sync error, index 0x%lx).", idx); | ||
1170 | return PTR_ERR(page); | ||
1171 | } | ||
1172 | wait_on_page_locked(page); | ||
1173 | if (unlikely(!PageUptodate(page))) { | ||
1174 | ntfs_error(vol->sb, "Failed to read first partial page " | ||
1175 | "(async error, index 0x%lx).", idx); | ||
1176 | page_cache_release(page); | ||
1177 | return PTR_ERR(page); | ||
1178 | } | ||
1179 | /* | ||
1180 | * If the last page is the same as the first page, need to | ||
1181 | * limit the write to the end offset. | ||
1182 | */ | ||
1183 | size = PAGE_CACHE_SIZE; | ||
1184 | if (idx == end) | ||
1185 | size = end_ofs; | ||
1186 | kaddr = kmap_atomic(page, KM_USER0); | ||
1187 | memset(kaddr + start_ofs, val, size - start_ofs); | ||
1188 | flush_dcache_page(page); | ||
1189 | kunmap_atomic(kaddr, KM_USER0); | ||
1190 | set_page_dirty(page); | ||
1191 | page_cache_release(page); | ||
1192 | if (idx == end) | ||
1193 | goto done; | ||
1194 | idx++; | ||
1195 | } | ||
1196 | /* Do the whole pages the fast way. */ | ||
1197 | for (; idx < end; idx++) { | ||
1198 | /* Find or create the current page. (The page is locked.) */ | ||
1199 | page = grab_cache_page(mapping, idx); | ||
1200 | if (unlikely(!page)) { | ||
1201 | ntfs_error(vol->sb, "Insufficient memory to grab " | ||
1202 | "page (index 0x%lx).", idx); | ||
1203 | return -ENOMEM; | ||
1204 | } | ||
1205 | kaddr = kmap_atomic(page, KM_USER0); | ||
1206 | memset(kaddr, val, PAGE_CACHE_SIZE); | ||
1207 | flush_dcache_page(page); | ||
1208 | kunmap_atomic(kaddr, KM_USER0); | ||
1209 | /* | ||
1210 | * If the page has buffers, mark them uptodate since buffer | ||
1211 | * state and not page state is definitive in 2.6 kernels. | ||
1212 | */ | ||
1213 | if (page_has_buffers(page)) { | ||
1214 | struct buffer_head *bh, *head; | ||
1215 | |||
1216 | bh = head = page_buffers(page); | ||
1217 | do { | ||
1218 | set_buffer_uptodate(bh); | ||
1219 | } while ((bh = bh->b_this_page) != head); | ||
1220 | } | ||
1221 | /* Now that buffers are uptodate, set the page uptodate, too. */ | ||
1222 | SetPageUptodate(page); | ||
1223 | /* | ||
1224 | * Set the page and all its buffers dirty and mark the inode | ||
1225 | * dirty, too. The VM will write the page later on. | ||
1226 | */ | ||
1227 | set_page_dirty(page); | ||
1228 | /* Finally unlock and release the page. */ | ||
1229 | unlock_page(page); | ||
1230 | page_cache_release(page); | ||
1231 | } | ||
1232 | /* If there is a last partial page, need to do it the slow way. */ | ||
1233 | if (end_ofs) { | ||
1234 | page = read_cache_page(mapping, idx, | ||
1235 | (filler_t*)mapping->a_ops->readpage, NULL); | ||
1236 | if (IS_ERR(page)) { | ||
1237 | ntfs_error(vol->sb, "Failed to read last partial page " | ||
1238 | "(sync error, index 0x%lx).", idx); | ||
1239 | return PTR_ERR(page); | ||
1240 | } | ||
1241 | wait_on_page_locked(page); | ||
1242 | if (unlikely(!PageUptodate(page))) { | ||
1243 | ntfs_error(vol->sb, "Failed to read last partial page " | ||
1244 | "(async error, index 0x%lx).", idx); | ||
1245 | page_cache_release(page); | ||
1246 | return PTR_ERR(page); | ||
1247 | } | ||
1248 | kaddr = kmap_atomic(page, KM_USER0); | ||
1249 | memset(kaddr, val, end_ofs); | ||
1250 | flush_dcache_page(page); | ||
1251 | kunmap_atomic(kaddr, KM_USER0); | ||
1252 | set_page_dirty(page); | ||
1253 | page_cache_release(page); | ||
1254 | } | ||
1255 | done: | ||
1256 | ntfs_debug("Done."); | ||
1257 | return 0; | ||
1258 | } | ||