1 |
1275 |
phoenix |
/*
|
2 |
|
|
* dir.c
|
3 |
|
|
*
|
4 |
|
|
* Copyright (C) 1995-1997, 1999 Martin von Löwis
|
5 |
|
|
* Copyright (C) 1999 Steve Dodd
|
6 |
|
|
* Copyright (C) 1999 Joseph Malicki
|
7 |
|
|
* Copyright (C) 2001 Anton Altaparmakov (AIA)
|
8 |
|
|
*/
|
9 |
|
|
|
10 |
|
|
#include "ntfstypes.h"
|
11 |
|
|
#include "struct.h"
|
12 |
|
|
#include "dir.h"
|
13 |
|
|
#include "macros.h"
|
14 |
|
|
|
15 |
|
|
#include <linux/errno.h>
|
16 |
|
|
#include "super.h"
|
17 |
|
|
#include "inode.h"
|
18 |
|
|
#include "attr.h"
|
19 |
|
|
#include "support.h"
|
20 |
|
|
#include "util.h"
|
21 |
|
|
#include <linux/smp_lock.h>
|
22 |
|
|
#include <linux/bitops.h>
|
23 |
|
|
|
24 |
|
|
static char I30[] = "$I30";
|
25 |
|
|
|
26 |
|
|
/* An index record should start with INDX, and the last word in each block
|
27 |
|
|
* should contain the check value. If it passes, the original values need to
|
28 |
|
|
* be restored. */
|
29 |
|
|
int ntfs_check_index_record(ntfs_inode *ino, char *record)
|
30 |
|
|
{
|
31 |
|
|
return ntfs_fixup_record(record, "INDX", ino->u.index.recordsize);
|
32 |
|
|
}
|
33 |
|
|
|
34 |
|
|
static inline int ntfs_is_top(ntfs_u64 stack)
|
35 |
|
|
{
|
36 |
|
|
return stack == 14;
|
37 |
|
|
}
|
38 |
|
|
|
39 |
|
|
static int ntfs_pop(ntfs_u64 *stack)
|
40 |
|
|
{
|
41 |
|
|
static int width[16] = {1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
|
42 |
|
|
int res = -1;
|
43 |
|
|
|
44 |
|
|
switch (width[*stack & 15]) {
|
45 |
|
|
case 1:
|
46 |
|
|
res = (int)((*stack & 15) >> 1);
|
47 |
|
|
*stack >>= 4;
|
48 |
|
|
break;
|
49 |
|
|
case 2:
|
50 |
|
|
res = (int)(((*stack & 63) >> 2) + 7);
|
51 |
|
|
*stack >>= 6;
|
52 |
|
|
break;
|
53 |
|
|
case 3:
|
54 |
|
|
res = (int)(((*stack & 255) >> 3) + 23);
|
55 |
|
|
*stack >>= 8;
|
56 |
|
|
break;
|
57 |
|
|
case 4:
|
58 |
|
|
res = (int)(((*stack & 1023) >> 4) + 55);
|
59 |
|
|
*stack >>= 10;
|
60 |
|
|
break;
|
61 |
|
|
default:
|
62 |
|
|
ntfs_error("Unknown encoding\n");
|
63 |
|
|
}
|
64 |
|
|
return res;
|
65 |
|
|
}
|
66 |
|
|
|
67 |
|
|
static inline unsigned int ntfs_top(void)
|
68 |
|
|
{
|
69 |
|
|
return 14;
|
70 |
|
|
}
|
71 |
|
|
|
72 |
|
|
static ntfs_u64 ntfs_push(ntfs_u64 stack, int i)
|
73 |
|
|
{
|
74 |
|
|
if (i < 7)
|
75 |
|
|
return (stack << 4) | (i << 1);
|
76 |
|
|
if (i < 23)
|
77 |
|
|
return (stack << 6) | ((i - 7) << 2) | 1;
|
78 |
|
|
if (i < 55)
|
79 |
|
|
return (stack << 8) | ((i - 23) << 3) | 3;
|
80 |
|
|
if (i < 120)
|
81 |
|
|
return (stack << 10) | ((i - 55) << 4) | 7;
|
82 |
|
|
ntfs_error("Too many entries\n");
|
83 |
|
|
return ~((ntfs_u64)0);
|
84 |
|
|
}
|
85 |
|
|
|
86 |
|
|
#if 0
|
87 |
|
|
static void ntfs_display_stack(ntfs_u64 stack)
|
88 |
|
|
{
|
89 |
|
|
while(!ntfs_is_top(stack))
|
90 |
|
|
{
|
91 |
|
|
printf("%d ", ntfs_pop(&stack));
|
92 |
|
|
}
|
93 |
|
|
printf("\n");
|
94 |
|
|
}
|
95 |
|
|
#endif
|
96 |
|
|
|
97 |
|
|
/* True if the entry points to another block of entries. */
|
98 |
|
|
static inline int ntfs_entry_has_subnodes(char *entry)
|
99 |
|
|
{
|
100 |
|
|
return (NTFS_GETU16(entry + 0xc) & 1);
|
101 |
|
|
}
|
102 |
|
|
|
103 |
|
|
/* True if it is not the 'end of dir' entry. */
|
104 |
|
|
static inline int ntfs_entry_is_used(char *entry)
|
105 |
|
|
{
|
106 |
|
|
return !(NTFS_GETU16(entry + 0xc) & 2);
|
107 |
|
|
}
|
108 |
|
|
|
109 |
|
|
/*
|
110 |
|
|
* Removed RACE for allocating index blocks. But stil not too happy.
|
111 |
|
|
* There might be more races afterwards. (AIA)
|
112 |
|
|
*/
|
113 |
|
|
static int ntfs_allocate_index_block(ntfs_iterate_s *walk)
|
114 |
|
|
{
|
115 |
|
|
ntfs_attribute *allocation, *bitmap = 0;
|
116 |
|
|
int error, size, i, bit;
|
117 |
|
|
ntfs_u8 *bmap;
|
118 |
|
|
ntfs_io io;
|
119 |
|
|
ntfs_volume *vol = walk->dir->vol;
|
120 |
|
|
|
121 |
|
|
/* Check for allocation attribute. */
|
122 |
|
|
allocation = ntfs_find_attr(walk->dir, vol->at_index_allocation, I30);
|
123 |
|
|
if (!allocation) {
|
124 |
|
|
ntfs_u8 bmp[8];
|
125 |
|
|
/* Create index allocation attribute. */
|
126 |
|
|
error = ntfs_create_attr(walk->dir, vol->at_index_allocation,
|
127 |
|
|
I30, 0, 0, &allocation);
|
128 |
|
|
if (error)
|
129 |
|
|
goto err_ret;
|
130 |
|
|
ntfs_bzero(bmp, sizeof(bmp));
|
131 |
|
|
error = ntfs_create_attr(walk->dir, vol->at_bitmap, I30, bmp,
|
132 |
|
|
sizeof(bmp), &bitmap);
|
133 |
|
|
if (error)
|
134 |
|
|
goto err_ret;
|
135 |
|
|
} else
|
136 |
|
|
bitmap = ntfs_find_attr(walk->dir, vol->at_bitmap, I30);
|
137 |
|
|
if (!bitmap) {
|
138 |
|
|
ntfs_error("Directory w/o bitmap\n");
|
139 |
|
|
error = -EINVAL;
|
140 |
|
|
goto err_ret;
|
141 |
|
|
}
|
142 |
|
|
size = bitmap->size;
|
143 |
|
|
bmap = ntfs_malloc(size);
|
144 |
|
|
if (!bmap) {
|
145 |
|
|
error = -ENOMEM;
|
146 |
|
|
goto err_ret;
|
147 |
|
|
}
|
148 |
|
|
io.fn_put = ntfs_put;
|
149 |
|
|
io.fn_get = ntfs_get;
|
150 |
|
|
try_again:
|
151 |
|
|
io.param = bmap;
|
152 |
|
|
io.size = size;
|
153 |
|
|
error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, 0, &io);
|
154 |
|
|
if (error || (io.size != size && (error = -EIO, 1)))
|
155 |
|
|
goto err_fb_out;
|
156 |
|
|
/* Allocate a bit. */
|
157 |
|
|
for (bit = i = 0; i < size; i++) {
|
158 |
|
|
if (bmap[i] == 0xFF)
|
159 |
|
|
continue;
|
160 |
|
|
bit = ffz(bmap[i]);
|
161 |
|
|
if (bit < 8)
|
162 |
|
|
break;
|
163 |
|
|
}
|
164 |
|
|
if (i >= size) {
|
165 |
|
|
/* FIXME: Extend bitmap. */
|
166 |
|
|
error = -EOPNOTSUPP;
|
167 |
|
|
goto err_fb_out;
|
168 |
|
|
}
|
169 |
|
|
/* Get the byte containing our bit again, now taking the BKL. */
|
170 |
|
|
io.param = bmap;
|
171 |
|
|
io.size = 1;
|
172 |
|
|
lock_kernel();
|
173 |
|
|
error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, i, &io);
|
174 |
|
|
if (error || (io.size != 1 && (error = -EIO, 1)))
|
175 |
|
|
goto err_unl_out;
|
176 |
|
|
if (ntfs_test_and_set_bit(bmap, bit)) {
|
177 |
|
|
unlock_kernel();
|
178 |
|
|
/* Give other process(es) a chance to finish. */
|
179 |
|
|
schedule();
|
180 |
|
|
goto try_again;
|
181 |
|
|
}
|
182 |
|
|
walk->newblock = (i * 8 + bit) * walk->dir->u.index.clusters_per_record;
|
183 |
|
|
io.param = bmap;
|
184 |
|
|
error = ntfs_write_attr(walk->dir, vol->at_bitmap, I30, i, &io);
|
185 |
|
|
if (error || (io.size != size && (error = -EIO, 1)))
|
186 |
|
|
goto err_unl_out;
|
187 |
|
|
/* Change inode on disk, required when bitmap is resident. */
|
188 |
|
|
error = ntfs_update_inode(walk->dir);
|
189 |
|
|
if (error)
|
190 |
|
|
goto err_unl_out;
|
191 |
|
|
unlock_kernel();
|
192 |
|
|
ntfs_free(bmap);
|
193 |
|
|
/* Check whether record is out of allocated range. */
|
194 |
|
|
size = allocation->size;
|
195 |
|
|
if (walk->newblock * vol->cluster_size >= size) {
|
196 |
|
|
/* Build index record. */
|
197 |
|
|
int hsize;
|
198 |
|
|
int s1 = walk->dir->u.index.recordsize;
|
199 |
|
|
int nr_fix = (s1 >> vol->sector_size) + 1;
|
200 |
|
|
char *record = ntfs_malloc(s1);
|
201 |
|
|
if (!record) {
|
202 |
|
|
error = -ENOMEM;
|
203 |
|
|
goto err_ret;
|
204 |
|
|
}
|
205 |
|
|
ntfs_bzero(record, s1);
|
206 |
|
|
/* Magic */
|
207 |
|
|
ntfs_memcpy(record, "INDX", 4);
|
208 |
|
|
/* Offset to fixups */
|
209 |
|
|
NTFS_PUTU16(record + 4, 0x28);
|
210 |
|
|
/* Number of fixups. */
|
211 |
|
|
NTFS_PUTU16(record + 6, nr_fix);
|
212 |
|
|
/* Log file sequence number - We don't do journalling so we
|
213 |
|
|
* just set it to zero which should be the Right Thing. (AIA) */
|
214 |
|
|
NTFS_PUTU64(record + 8, 0);
|
215 |
|
|
/* VCN of buffer */
|
216 |
|
|
NTFS_PUTU64(record + 0x10, walk->newblock);
|
217 |
|
|
/* Header size. */
|
218 |
|
|
hsize = 0x10 + 2 * nr_fix;
|
219 |
|
|
hsize = (hsize + 7) & ~7; /* Align. */
|
220 |
|
|
NTFS_PUTU16(record + 0x18, hsize);
|
221 |
|
|
/* Total size of record. */
|
222 |
|
|
NTFS_PUTU32(record + 0x20, s1 - 0x18);
|
223 |
|
|
/* Writing the data will extend the attribute. */
|
224 |
|
|
io.param = record;
|
225 |
|
|
io.size = s1;
|
226 |
|
|
io.do_read = 0;
|
227 |
|
|
error = ntfs_readwrite_attr(walk->dir, allocation, size, &io);
|
228 |
|
|
ntfs_free(record);
|
229 |
|
|
if (error || (io.size != s1 && (error = -EIO, 1)))
|
230 |
|
|
goto err_ret;
|
231 |
|
|
error = ntfs_update_inode(walk->dir);
|
232 |
|
|
if (error)
|
233 |
|
|
goto err_ret;
|
234 |
|
|
}
|
235 |
|
|
return 0;
|
236 |
|
|
err_unl_out:
|
237 |
|
|
unlock_kernel();
|
238 |
|
|
err_fb_out:
|
239 |
|
|
ntfs_free(bmap);
|
240 |
|
|
err_ret:
|
241 |
|
|
return error;
|
242 |
|
|
}
|
243 |
|
|
|
244 |
|
|
/* Write an index block (root or allocation) back to storage.
|
245 |
|
|
* Used is the total number of bytes in buf, including all headers. */
|
246 |
|
|
static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
|
247 |
|
|
int used)
|
248 |
|
|
{
|
249 |
|
|
ntfs_io io;
|
250 |
|
|
int error;
|
251 |
|
|
ntfs_attribute *a;
|
252 |
|
|
ntfs_volume *vol = walk->dir->vol;
|
253 |
|
|
|
254 |
|
|
io.fn_put = 0;
|
255 |
|
|
io.fn_get = ntfs_get;
|
256 |
|
|
io.param = buf;
|
257 |
|
|
if (block == -1) { /* Index root. */
|
258 |
|
|
NTFS_PUTU16(buf + 0x14, used - 0x10);
|
259 |
|
|
/* 0x18 is a copy thereof. */
|
260 |
|
|
NTFS_PUTU16(buf + 0x18, used - 0x10);
|
261 |
|
|
io.size = used;
|
262 |
|
|
error = ntfs_write_attr(walk->dir, vol->at_index_root, I30, 0,
|
263 |
|
|
&io);
|
264 |
|
|
if (error || (io.size != used && (error = -EIO, 1)))
|
265 |
|
|
return error;
|
266 |
|
|
/* Shrink if necessary. */
|
267 |
|
|
a = ntfs_find_attr(walk->dir, vol->at_index_root, I30);
|
268 |
|
|
ntfs_resize_attr(walk->dir, a, used);
|
269 |
|
|
} else {
|
270 |
|
|
NTFS_PUTU16(buf + 0x1C, used - 0x18);
|
271 |
|
|
io.size = walk->dir->u.index.recordsize;
|
272 |
|
|
error = ntfs_insert_fixups(buf, io.size);
|
273 |
|
|
if (error) {
|
274 |
|
|
printk(KERN_ALERT "NTFS: ntfs_index_writeback() caught "
|
275 |
|
|
"corrupt index record ntfs record "
|
276 |
|
|
"header. Refusing to write corrupt "
|
277 |
|
|
"data to disk. Unmount and run chkdsk "
|
278 |
|
|
"immediately!\n");
|
279 |
|
|
return -EIO;
|
280 |
|
|
}
|
281 |
|
|
error = ntfs_write_attr(walk->dir, vol->at_index_allocation,
|
282 |
|
|
I30, (__s64)block << vol->cluster_size_bits,
|
283 |
|
|
&io);
|
284 |
|
|
if (error || (io.size != walk->dir->u.index.recordsize &&
|
285 |
|
|
(error = -EIO, 1)))
|
286 |
|
|
return error;
|
287 |
|
|
}
|
288 |
|
|
return 0;
|
289 |
|
|
}
|
290 |
|
|
|
291 |
|
|
static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize,
|
292 |
|
|
int usize)
|
293 |
|
|
{
|
294 |
|
|
char *entry, *prev;
|
295 |
|
|
ntfs_u8 *newbuf = 0, *middle = 0;
|
296 |
|
|
int error, othersize, mlen;
|
297 |
|
|
ntfs_io io;
|
298 |
|
|
ntfs_volume *vol = walk->dir->vol;
|
299 |
|
|
int oldblock;
|
300 |
|
|
|
301 |
|
|
error = ntfs_allocate_index_block(walk);
|
302 |
|
|
if (error)
|
303 |
|
|
return error;
|
304 |
|
|
/* This should not happen. */
|
305 |
|
|
if (walk->block == -1) {
|
306 |
|
|
ntfs_error("Trying to split root");
|
307 |
|
|
return -EOPNOTSUPP;
|
308 |
|
|
}
|
309 |
|
|
entry = start + NTFS_GETU16(start + 0x18) + 0x18;
|
310 |
|
|
for (prev = entry; entry - start < usize / 2;
|
311 |
|
|
entry += NTFS_GETU16(entry + 8))
|
312 |
|
|
prev = entry;
|
313 |
|
|
newbuf = ntfs_malloc(vol->index_record_size);
|
314 |
|
|
if (!newbuf)
|
315 |
|
|
return -ENOMEM;
|
316 |
|
|
io.fn_put = ntfs_put;
|
317 |
|
|
io.fn_get = ntfs_get;
|
318 |
|
|
io.param = newbuf;
|
319 |
|
|
io.size = vol->index_record_size;
|
320 |
|
|
/* Read in old header. FIXME: Reading everything is overkill. */
|
321 |
|
|
error = ntfs_read_attr(walk->dir, vol->at_index_allocation, I30,
|
322 |
|
|
(__s64)walk->newblock << vol->cluster_size_bits, &io);
|
323 |
|
|
if (error)
|
324 |
|
|
goto out;
|
325 |
|
|
if (io.size != vol->index_record_size) {
|
326 |
|
|
error = -EIO;
|
327 |
|
|
goto out;
|
328 |
|
|
}
|
329 |
|
|
/* FIXME: Adjust header. */
|
330 |
|
|
/* Copy everything from entry to new block. */
|
331 |
|
|
othersize = usize - (entry - start);
|
332 |
|
|
ntfs_memcpy(newbuf + NTFS_GETU16(newbuf + 0x18) + 0x18, entry,
|
333 |
|
|
othersize);
|
334 |
|
|
/* Copy flags. */
|
335 |
|
|
NTFS_PUTU32(newbuf + 0x24, NTFS_GETU32(start + 0x24));
|
336 |
|
|
error = ntfs_index_writeback(walk, newbuf, walk->newblock,
|
337 |
|
|
othersize + NTFS_GETU16(newbuf + 0x18) + 0x18);
|
338 |
|
|
if (error)
|
339 |
|
|
goto out;
|
340 |
|
|
/* Move prev to walk. */
|
341 |
|
|
mlen = NTFS_GETU16(prev + 0x8);
|
342 |
|
|
/* Remember old child node. */
|
343 |
|
|
if (ntfs_entry_has_subnodes(prev))
|
344 |
|
|
oldblock = NTFS_GETU32(prev + mlen - 8);
|
345 |
|
|
else
|
346 |
|
|
oldblock = -1;
|
347 |
|
|
/* Allow for pointer to subnode. */
|
348 |
|
|
middle = ntfs_malloc(ntfs_entry_has_subnodes(prev) ? mlen : mlen + 8);
|
349 |
|
|
if (!middle){
|
350 |
|
|
error = -ENOMEM;
|
351 |
|
|
goto out;
|
352 |
|
|
}
|
353 |
|
|
ntfs_memcpy(middle, prev, mlen);
|
354 |
|
|
/* Set has_subnodes flag. */
|
355 |
|
|
NTFS_PUTU8(middle + 0xC, NTFS_GETU8(middle + 0xC) | 1);
|
356 |
|
|
/* Middle entry points to block, parent entry will point to newblock. */
|
357 |
|
|
NTFS_PUTU64(middle + mlen - 8, walk->block);
|
358 |
|
|
if (walk->new_entry)
|
359 |
|
|
ntfs_error("Entry not reset");
|
360 |
|
|
walk->new_entry = middle;
|
361 |
|
|
walk->u.flags |= ITERATE_SPLIT_DONE;
|
362 |
|
|
/* Terminate old block. */
|
363 |
|
|
othersize = usize - (prev-start);
|
364 |
|
|
NTFS_PUTU64(prev, 0);
|
365 |
|
|
if (oldblock == -1) {
|
366 |
|
|
NTFS_PUTU32(prev + 8, 0x10);
|
367 |
|
|
NTFS_PUTU32(prev + 0xC, 2);
|
368 |
|
|
othersize += 0x10;
|
369 |
|
|
} else {
|
370 |
|
|
NTFS_PUTU32(prev + 8, 0x18);
|
371 |
|
|
NTFS_PUTU32(prev + 0xC, 3);
|
372 |
|
|
NTFS_PUTU64(prev + 0x10, oldblock);
|
373 |
|
|
othersize += 0x18;
|
374 |
|
|
}
|
375 |
|
|
/* Write back original block. */
|
376 |
|
|
error = ntfs_index_writeback(walk, start, walk->block, othersize);
|
377 |
|
|
out:
|
378 |
|
|
if (newbuf)
|
379 |
|
|
ntfs_free(newbuf);
|
380 |
|
|
if (middle)
|
381 |
|
|
ntfs_free(middle);
|
382 |
|
|
return error;
|
383 |
|
|
}
|
384 |
|
|
|
385 |
|
|
static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry)
|
386 |
|
|
{
|
387 |
|
|
int blocksize, usedsize, error, offset;
|
388 |
|
|
int do_split = 0;
|
389 |
|
|
offset = entry - start;
|
390 |
|
|
if (walk->block == -1) { /* index root */
|
391 |
|
|
blocksize = walk->dir->vol->mft_record_size;
|
392 |
|
|
usedsize = NTFS_GETU16(start + 0x14) + 0x10;
|
393 |
|
|
} else {
|
394 |
|
|
blocksize = walk->dir->u.index.recordsize;
|
395 |
|
|
usedsize = NTFS_GETU16(start + 0x1C) + 0x18;
|
396 |
|
|
}
|
397 |
|
|
if (usedsize + walk->new_entry_size > blocksize) {
|
398 |
|
|
char* s1 = ntfs_malloc(blocksize + walk->new_entry_size);
|
399 |
|
|
if (!s1)
|
400 |
|
|
return -ENOMEM;
|
401 |
|
|
ntfs_memcpy(s1, start, usedsize);
|
402 |
|
|
do_split = 1;
|
403 |
|
|
/* Adjust entry to s1. */
|
404 |
|
|
entry = s1 + (entry - start);
|
405 |
|
|
start = s1;
|
406 |
|
|
}
|
407 |
|
|
ntfs_memmove(entry + walk->new_entry_size, entry, usedsize - offset);
|
408 |
|
|
ntfs_memcpy(entry, walk->new_entry, walk->new_entry_size);
|
409 |
|
|
usedsize += walk->new_entry_size;
|
410 |
|
|
ntfs_free(walk->new_entry);
|
411 |
|
|
walk->new_entry = 0;
|
412 |
|
|
if (do_split) {
|
413 |
|
|
error = ntfs_split_record(walk, start, blocksize, usedsize);
|
414 |
|
|
ntfs_free(start);
|
415 |
|
|
} else {
|
416 |
|
|
error = ntfs_index_writeback(walk, start, walk->block,usedsize);
|
417 |
|
|
if (error)
|
418 |
|
|
return error;
|
419 |
|
|
}
|
420 |
|
|
return 0;
|
421 |
|
|
}
|
422 |
|
|
|
423 |
|
|
/* Try to split INDEX_ROOT attributes. Return -E2BIG if nothing changed. */
|
424 |
|
|
int ntfs_split_indexroot(ntfs_inode *ino)
|
425 |
|
|
{
|
426 |
|
|
ntfs_attribute *ra;
|
427 |
|
|
ntfs_u8 *root = 0, *index = 0;
|
428 |
|
|
ntfs_io io;
|
429 |
|
|
int error, off, i, bsize, isize;
|
430 |
|
|
ntfs_iterate_s walk;
|
431 |
|
|
|
432 |
|
|
ra = ntfs_find_attr(ino, ino->vol->at_index_root, I30);
|
433 |
|
|
if (!ra)
|
434 |
|
|
return -ENOTDIR;
|
435 |
|
|
bsize = ino->vol->mft_record_size;
|
436 |
|
|
root = ntfs_malloc(bsize);
|
437 |
|
|
if (!root)
|
438 |
|
|
return -E2BIG;
|
439 |
|
|
io.fn_put = ntfs_put;
|
440 |
|
|
io.param = root;
|
441 |
|
|
io.size = bsize;
|
442 |
|
|
error = ntfs_read_attr(ino, ino->vol->at_index_root, I30, 0, &io);
|
443 |
|
|
if (error)
|
444 |
|
|
goto out;
|
445 |
|
|
off = 0x20;
|
446 |
|
|
/* Count number of entries. */
|
447 |
|
|
for (i = 0; ntfs_entry_is_used(root + off); i++)
|
448 |
|
|
off += NTFS_GETU16(root + off + 8);
|
449 |
|
|
if (i <= 2) {
|
450 |
|
|
/* We don't split small index roots. */
|
451 |
|
|
error = -E2BIG;
|
452 |
|
|
goto out;
|
453 |
|
|
}
|
454 |
|
|
index = ntfs_malloc(ino->vol->index_record_size);
|
455 |
|
|
if (!index) {
|
456 |
|
|
error = -ENOMEM;
|
457 |
|
|
goto out;
|
458 |
|
|
}
|
459 |
|
|
walk.dir = ino;
|
460 |
|
|
walk.block = -1;
|
461 |
|
|
walk.result = walk.new_entry = 0;
|
462 |
|
|
walk.name = 0;
|
463 |
|
|
error = ntfs_allocate_index_block(&walk);
|
464 |
|
|
if (error)
|
465 |
|
|
goto out;
|
466 |
|
|
/* Write old root to new index block. */
|
467 |
|
|
io.param = index;
|
468 |
|
|
io.size = ino->vol->index_record_size;
|
469 |
|
|
error = ntfs_read_attr(ino, ino->vol->at_index_allocation, I30,
|
470 |
|
|
(__s64)walk.newblock << ino->vol->cluster_size_bits, &io);
|
471 |
|
|
if (error)
|
472 |
|
|
goto out;
|
473 |
|
|
isize = NTFS_GETU16(root + 0x18) - 0x10;
|
474 |
|
|
ntfs_memcpy(index + NTFS_GETU16(index + 0x18) + 0x18, root+0x20, isize);
|
475 |
|
|
/* Copy flags. */
|
476 |
|
|
NTFS_PUTU32(index + 0x24, NTFS_GETU32(root + 0x1C));
|
477 |
|
|
error = ntfs_index_writeback(&walk, index, walk.newblock,
|
478 |
|
|
isize + NTFS_GETU16(index + 0x18) + 0x18);
|
479 |
|
|
if (error)
|
480 |
|
|
goto out;
|
481 |
|
|
/* Mark root as split. */
|
482 |
|
|
NTFS_PUTU32(root + 0x1C, 1);
|
483 |
|
|
/* Truncate index root. */
|
484 |
|
|
NTFS_PUTU64(root + 0x20, 0);
|
485 |
|
|
NTFS_PUTU32(root + 0x28, 0x18);
|
486 |
|
|
NTFS_PUTU32(root + 0x2C, 3);
|
487 |
|
|
NTFS_PUTU64(root + 0x30, walk.newblock);
|
488 |
|
|
error = ntfs_index_writeback(&walk, root, -1, 0x38);
|
489 |
|
|
out:
|
490 |
|
|
ntfs_free(root);
|
491 |
|
|
ntfs_free(index);
|
492 |
|
|
return error;
|
493 |
|
|
}
|
494 |
|
|
|
495 |
|
|
/* The entry has been found. Copy the result in the caller's buffer */
|
496 |
|
|
static int ntfs_copyresult(char *dest, char *source)
|
497 |
|
|
{
|
498 |
|
|
int length = NTFS_GETU16(source + 8);
|
499 |
|
|
ntfs_memcpy(dest, source, length);
|
500 |
|
|
return 1;
|
501 |
|
|
}
|
502 |
|
|
|
503 |
|
|
/* Use $UpCase some day. */
|
504 |
|
|
static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x)
|
505 |
|
|
{
|
506 |
|
|
/* We should read any pending rest of $UpCase here. */
|
507 |
|
|
if (x >= vol->upcase_length)
|
508 |
|
|
return x;
|
509 |
|
|
return vol->upcase[x];
|
510 |
|
|
}
|
511 |
|
|
|
512 |
|
|
/* Everything passed in walk and entry. */
|
513 |
|
|
static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry)
|
514 |
|
|
{
|
515 |
|
|
int lu = *(entry + 0x50);
|
516 |
|
|
int i;
|
517 |
|
|
|
518 |
|
|
ntfs_u16* name = (ntfs_u16*)(entry + 0x52);
|
519 |
|
|
ntfs_volume *vol = walk->dir->vol;
|
520 |
|
|
for (i = 0; i < lu && i < walk->namelen; i++)
|
521 |
|
|
if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) !=
|
522 |
|
|
ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
|
523 |
|
|
break;
|
524 |
|
|
if (i == lu && i == walk->namelen)
|
525 |
|
|
return 0;
|
526 |
|
|
if (i == lu)
|
527 |
|
|
return 1;
|
528 |
|
|
if (i == walk->namelen)
|
529 |
|
|
return -1;
|
530 |
|
|
if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) <
|
531 |
|
|
ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
|
532 |
|
|
return 1;
|
533 |
|
|
return -1;
|
534 |
|
|
}
|
535 |
|
|
|
536 |
|
|
/* Necessary forward declaration. */
|
537 |
|
|
static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry);
|
538 |
|
|
|
539 |
|
|
/* Parse a block of entries. Load the block, fix it up, and iterate over the
|
540 |
|
|
* entries. The block is given as virtual cluster number. */
|
541 |
|
|
static int ntfs_getdir_record(ntfs_iterate_s *walk, int block)
|
542 |
|
|
{
|
543 |
|
|
int length = walk->dir->u.index.recordsize;
|
544 |
|
|
char *record = (char*)ntfs_malloc(length);
|
545 |
|
|
char *offset;
|
546 |
|
|
int retval,error;
|
547 |
|
|
int oldblock;
|
548 |
|
|
ntfs_io io;
|
549 |
|
|
|
550 |
|
|
if (!record)
|
551 |
|
|
return -ENOMEM;
|
552 |
|
|
io.fn_put = ntfs_put;
|
553 |
|
|
io.param = record;
|
554 |
|
|
io.size = length;
|
555 |
|
|
/* Read the block from the index allocation attribute. */
|
556 |
|
|
error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_allocation,
|
557 |
|
|
I30, (__s64)block << walk->dir->vol->cluster_size_bits, &io);
|
558 |
|
|
if (error || io.size != length) {
|
559 |
|
|
ntfs_error("read failed\n");
|
560 |
|
|
ntfs_free(record);
|
561 |
|
|
return 0;
|
562 |
|
|
}
|
563 |
|
|
if (!ntfs_check_index_record(walk->dir, record)) {
|
564 |
|
|
ntfs_error("%x is not an index record\n", block);
|
565 |
|
|
ntfs_free(record);
|
566 |
|
|
return 0;
|
567 |
|
|
}
|
568 |
|
|
offset = record + NTFS_GETU16(record + 0x18) + 0x18;
|
569 |
|
|
oldblock = walk->block;
|
570 |
|
|
walk->block = block;
|
571 |
|
|
retval = ntfs_getdir_iterate(walk, record, offset);
|
572 |
|
|
walk->block = oldblock;
|
573 |
|
|
ntfs_free(record);
|
574 |
|
|
return retval;
|
575 |
|
|
}
|
576 |
|
|
|
577 |
|
|
/* Go down to the next block of entries. These collate before the current
|
578 |
|
|
* entry. */
|
579 |
|
|
static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry)
|
580 |
|
|
{
|
581 |
|
|
int length = NTFS_GETU16(entry + 8);
|
582 |
|
|
int nextblock = NTFS_GETU32(entry + length - 8);
|
583 |
|
|
int error;
|
584 |
|
|
|
585 |
|
|
if (!ntfs_entry_has_subnodes(entry)) {
|
586 |
|
|
ntfs_error("illegal ntfs_descend call\n");
|
587 |
|
|
return 0;
|
588 |
|
|
}
|
589 |
|
|
error = ntfs_getdir_record(walk, nextblock);
|
590 |
|
|
if (!error && walk->type == DIR_INSERT &&
|
591 |
|
|
(walk->u.flags & ITERATE_SPLIT_DONE)) {
|
592 |
|
|
/* Split has occurred. Adjust entry, insert new_entry. */
|
593 |
|
|
NTFS_PUTU32(entry + length - 8, walk->newblock);
|
594 |
|
|
/* Reset flags, as the current block might be split again. */
|
595 |
|
|
walk->u.flags &= ~ITERATE_SPLIT_DONE;
|
596 |
|
|
error = ntfs_dir_insert(walk, start, entry);
|
597 |
|
|
}
|
598 |
|
|
return error;
|
599 |
|
|
}
|
600 |
|
|
|
601 |
|
|
static int ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk, char* start,
|
602 |
|
|
char *entry)
|
603 |
|
|
{
|
604 |
|
|
int retval = 0;
|
605 |
|
|
int curpos = 0, destpos = 0;
|
606 |
|
|
int length;
|
607 |
|
|
if (walk->u.pos != 0) {
|
608 |
|
|
if (ntfs_is_top(walk->u.pos))
|
609 |
|
|
return 0;
|
610 |
|
|
destpos = ntfs_pop(&walk->u.pos);
|
611 |
|
|
}
|
612 |
|
|
while (1) {
|
613 |
|
|
if (walk->u.pos == 0) {
|
614 |
|
|
if (ntfs_entry_has_subnodes(entry))
|
615 |
|
|
ntfs_descend(walk, start, entry);
|
616 |
|
|
else
|
617 |
|
|
walk->u.pos = ntfs_top();
|
618 |
|
|
if (ntfs_is_top(walk->u.pos) &&
|
619 |
|
|
!ntfs_entry_is_used(entry))
|
620 |
|
|
return 1;
|
621 |
|
|
walk->u.pos = ntfs_push(walk->u.pos, curpos);
|
622 |
|
|
return 1;
|
623 |
|
|
}
|
624 |
|
|
if (curpos == destpos) {
|
625 |
|
|
if (!ntfs_is_top(walk->u.pos) &&
|
626 |
|
|
ntfs_entry_has_subnodes(entry)) {
|
627 |
|
|
retval = ntfs_descend(walk, start, entry);
|
628 |
|
|
if (retval) {
|
629 |
|
|
walk->u.pos = ntfs_push(walk->u.pos,
|
630 |
|
|
curpos);
|
631 |
|
|
return retval;
|
632 |
|
|
}
|
633 |
|
|
if (!ntfs_entry_is_used(entry))
|
634 |
|
|
return 0;
|
635 |
|
|
walk->u.pos = 0;
|
636 |
|
|
}
|
637 |
|
|
if (ntfs_entry_is_used(entry)) {
|
638 |
|
|
retval = ntfs_copyresult(walk->result, entry);
|
639 |
|
|
walk->u.pos = 0;
|
640 |
|
|
} else {
|
641 |
|
|
walk->u.pos = ntfs_top();
|
642 |
|
|
return 0;
|
643 |
|
|
}
|
644 |
|
|
}
|
645 |
|
|
curpos++;
|
646 |
|
|
if (!ntfs_entry_is_used(entry))
|
647 |
|
|
break;
|
648 |
|
|
length = NTFS_GETU16(entry + 8);
|
649 |
|
|
if (!length) {
|
650 |
|
|
ntfs_error("infinite loop\n");
|
651 |
|
|
break;
|
652 |
|
|
}
|
653 |
|
|
entry += length;
|
654 |
|
|
}
|
655 |
|
|
return -1;
|
656 |
|
|
}
|
657 |
|
|
|
658 |
|
|
/* Iterate over a list of entries, either from an index block, or from the
|
659 |
|
|
* index root.
|
660 |
|
|
* If searching BY_POSITION, pop the top index from the position. If the
|
661 |
|
|
* position stack is empty then, return the item at the index and set the
|
662 |
|
|
* position to the next entry. If the position stack is not empty,
|
663 |
|
|
* recursively proceed for subnodes. If the entry at the position is the
|
664 |
|
|
* 'end of dir' entry, return 'not found' and the empty stack.
|
665 |
|
|
* If searching BY_NAME, walk through the items until found or until
|
666 |
|
|
* one item is collated after the requested item. In the former case, return
|
667 |
|
|
* the result. In the latter case, recursively proceed to the subnodes.
|
668 |
|
|
* If 'end of dir' is reached, the name is not in the directory */
|
669 |
|
|
static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry)
|
670 |
|
|
{
|
671 |
|
|
int length;
|
672 |
|
|
int cmp;
|
673 |
|
|
|
674 |
|
|
if (walk->type == BY_POSITION)
|
675 |
|
|
return ntfs_getdir_iterate_byposition(walk, start, entry);
|
676 |
|
|
do {
|
677 |
|
|
/* If the current entry is a real one, compare with the
|
678 |
|
|
* requested item. If the current entry is the last item, it
|
679 |
|
|
* is always larger than the requested item. */
|
680 |
|
|
cmp = ntfs_entry_is_used(entry) ?
|
681 |
|
|
ntfs_my_strcmp(walk,entry) : -1;
|
682 |
|
|
switch (walk->type) {
|
683 |
|
|
case BY_NAME:
|
684 |
|
|
switch (cmp) {
|
685 |
|
|
case -1:
|
686 |
|
|
return ntfs_entry_has_subnodes(entry) ?
|
687 |
|
|
ntfs_descend(walk, start, entry) : 0;
|
688 |
|
|
case 0:
|
689 |
|
|
return ntfs_copyresult(walk->result, entry);
|
690 |
|
|
case 1:
|
691 |
|
|
break;
|
692 |
|
|
}
|
693 |
|
|
break;
|
694 |
|
|
case DIR_INSERT:
|
695 |
|
|
switch (cmp) {
|
696 |
|
|
case -1:
|
697 |
|
|
return ntfs_entry_has_subnodes(entry) ?
|
698 |
|
|
ntfs_descend(walk, start, entry) :
|
699 |
|
|
ntfs_dir_insert(walk, start, entry);
|
700 |
|
|
case 0:
|
701 |
|
|
return -EEXIST;
|
702 |
|
|
case 1:
|
703 |
|
|
break;
|
704 |
|
|
}
|
705 |
|
|
break;
|
706 |
|
|
default:
|
707 |
|
|
ntfs_error("TODO\n"); /* FIXME: ? */
|
708 |
|
|
}
|
709 |
|
|
if (!ntfs_entry_is_used(entry))
|
710 |
|
|
break;
|
711 |
|
|
length = NTFS_GETU16(entry + 8);
|
712 |
|
|
if (!length) {
|
713 |
|
|
ntfs_error("infinite loop\n");
|
714 |
|
|
break;
|
715 |
|
|
}
|
716 |
|
|
entry += length;
|
717 |
|
|
} while (1);
|
718 |
|
|
return 0;
|
719 |
|
|
}
|
720 |
|
|
|
721 |
|
|
/* Tree walking is done using position numbers. The following numbers have a
|
722 |
|
|
* special meaning:
|
723 |
|
|
* 0 start (.)
|
724 |
|
|
* -1 no more entries
|
725 |
|
|
* -2 ..
|
726 |
|
|
* All other numbers encode sequences of indices. The sequence a, b, c is
|
727 |
|
|
* encoded as <stop><c><b><a>, where <foo> is the encoding of foo. The
|
728 |
|
|
* first few integers are encoded as follows:
|
729 |
|
|
* 0: 0000 1: 0010 2: 0100 3: 0110
|
730 |
|
|
* 4: 1000 5: 1010 6: 1100 stop: 1110
|
731 |
|
|
* 7: 000001 8: 000101 9: 001001 10: 001101
|
732 |
|
|
* The least significant bits give the width of this encoding, the other bits
|
733 |
|
|
* encode the value, starting from the first value of the interval.
|
734 |
|
|
* tag width first value last value
|
735 |
|
|
* 0 3 0 6
|
736 |
|
|
* 01 4 7 22
|
737 |
|
|
* 011 5 23 54
|
738 |
|
|
* 0111 6 55 119
|
739 |
|
|
* More values are hopefully not needed, as the file position has currently
|
740 |
|
|
* 64 bits in total. */
|
741 |
|
|
|
742 |
|
|
/* Find an entry in the directory. Return 0 if not found, otherwise copy the
|
743 |
|
|
* entry to the result buffer. */
|
744 |
|
|
int ntfs_getdir(ntfs_iterate_s *walk)
|
745 |
|
|
{
|
746 |
|
|
int length = walk->dir->vol->mft_record_size;
|
747 |
|
|
int retval, error;
|
748 |
|
|
/* Start at the index root. */
|
749 |
|
|
char *root = ntfs_malloc(length);
|
750 |
|
|
ntfs_io io;
|
751 |
|
|
|
752 |
|
|
if (!root)
|
753 |
|
|
return -ENOMEM;
|
754 |
|
|
io.fn_put = ntfs_put;
|
755 |
|
|
io.param = root;
|
756 |
|
|
io.size = length;
|
757 |
|
|
error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_root, I30,
|
758 |
|
|
0, &io);
|
759 |
|
|
if (error) {
|
760 |
|
|
ntfs_error("Not a directory\n");
|
761 |
|
|
return 0;
|
762 |
|
|
}
|
763 |
|
|
walk->block = -1;
|
764 |
|
|
/* FIXME: Move these to walk. */
|
765 |
|
|
walk->dir->u.index.recordsize = NTFS_GETU32(root + 0x8);
|
766 |
|
|
walk->dir->u.index.clusters_per_record = NTFS_GETU32(root + 0xC);
|
767 |
|
|
/* FIXME: Consistency check. */
|
768 |
|
|
/* Skip header. */
|
769 |
|
|
retval = ntfs_getdir_iterate(walk, root, root + 0x20);
|
770 |
|
|
ntfs_free(root);
|
771 |
|
|
return retval;
|
772 |
|
|
}
|
773 |
|
|
|
774 |
|
|
/* Find an entry in the directory by its position stack. Iteration starts
|
775 |
|
|
* if the stack is 0, in which case the position is set to the first item
|
776 |
|
|
* in the directory. If the position is nonzero, return the item at the
|
777 |
|
|
* position and change the position to the next item. The position is -1
|
778 |
|
|
* if there are no more items. */
|
779 |
|
|
int ntfs_getdir_byposition(ntfs_iterate_s *walk)
|
780 |
|
|
{
|
781 |
|
|
walk->type = BY_POSITION;
|
782 |
|
|
return ntfs_getdir(walk);
|
783 |
|
|
}
|
784 |
|
|
|
785 |
|
|
/* Find an entry in the directory by its name. Return 0 if not found. */
|
786 |
|
|
int ntfs_getdir_byname(ntfs_iterate_s *walk)
|
787 |
|
|
{
|
788 |
|
|
walk->type = BY_NAME;
|
789 |
|
|
return ntfs_getdir(walk);
|
790 |
|
|
}
|
791 |
|
|
|
792 |
|
|
int ntfs_getdir_unsorted(ntfs_inode *ino, u32 *p_high, u32 *p_low,
|
793 |
|
|
int (*cb)(ntfs_u8 *, void *), void *param)
|
794 |
|
|
{
|
795 |
|
|
s64 ib_ofs;
|
796 |
|
|
char *buf = 0, *entry = 0;
|
797 |
|
|
ntfs_attribute *attr;
|
798 |
|
|
ntfs_volume *vol;
|
799 |
|
|
int byte, bit, err = 0;
|
800 |
|
|
u32 start, finish, ibs, max_size;
|
801 |
|
|
ntfs_io io;
|
802 |
|
|
u8 ibs_bits;
|
803 |
|
|
|
804 |
|
|
if (!ino) {
|
805 |
|
|
ntfs_error("%s(): No inode! Returning -EINVAL.\n",__FUNCTION__);
|
806 |
|
|
return -EINVAL;
|
807 |
|
|
}
|
808 |
|
|
vol = ino->vol;
|
809 |
|
|
if (!vol) {
|
810 |
|
|
ntfs_error("%s(): Inode 0x%lx has no volume. Returning "
|
811 |
|
|
"-EINVAL.\n", __FUNCTION__, ino->i_number);
|
812 |
|
|
return -EINVAL;
|
813 |
|
|
}
|
814 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 1: Entering for inode 0x%lx, "
|
815 |
|
|
"p_high = 0x%x, p_low = 0x%x.\n", __FUNCTION__,
|
816 |
|
|
ino->i_number, *p_high, *p_low);
|
817 |
|
|
if (!*p_high) {
|
818 |
|
|
/* We are still in the index root. */
|
819 |
|
|
buf = ntfs_malloc(io.size = vol->mft_record_size);
|
820 |
|
|
if (!buf)
|
821 |
|
|
return -ENOMEM;
|
822 |
|
|
io.fn_put = ntfs_put;
|
823 |
|
|
io.param = buf;
|
824 |
|
|
err = ntfs_read_attr(ino, vol->at_index_root, I30, 0, &io);
|
825 |
|
|
if (err || !io.size)
|
826 |
|
|
goto read_err_ret;
|
827 |
|
|
ino->u.index.recordsize = ibs = NTFS_GETU32(buf + 0x8);
|
828 |
|
|
ino->u.index.clusters_per_record = NTFS_GETU32(buf + 0xC);
|
829 |
|
|
entry = buf + 0x20;
|
830 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 2: In index root.\n",
|
831 |
|
|
__FUNCTION__);
|
832 |
|
|
ibs_bits = ffs(ibs) - 1;
|
833 |
|
|
/* Compensate for faked "." and "..". */
|
834 |
|
|
start = 2;
|
835 |
|
|
} else { /* We are in an index record. */
|
836 |
|
|
io.size = ibs = ino->u.index.recordsize;
|
837 |
|
|
buf = ntfs_malloc(ibs);
|
838 |
|
|
if (!buf)
|
839 |
|
|
return -ENOMEM;
|
840 |
|
|
ibs_bits = ffs(ibs) - 1;
|
841 |
|
|
io.fn_put = ntfs_put;
|
842 |
|
|
io.param = buf;
|
843 |
|
|
/*
|
844 |
|
|
* 0 is index root, index allocation starts at 1 and works in
|
845 |
|
|
* units of index block size (ibs).
|
846 |
|
|
*/
|
847 |
|
|
ib_ofs = (s64)(*p_high - 1) << ibs_bits;
|
848 |
|
|
err = ntfs_read_attr(ino, vol->at_index_allocation, I30, ib_ofs,
|
849 |
|
|
&io);
|
850 |
|
|
if (err || io.size != ibs)
|
851 |
|
|
goto read_err_ret;
|
852 |
|
|
if (!ntfs_check_index_record(ino, buf)) {
|
853 |
|
|
ntfs_error("%s(): Index block 0x%x is not an index "
|
854 |
|
|
"record. Returning -ENOTDIR.\n",
|
855 |
|
|
__FUNCTION__, *p_high - 1);
|
856 |
|
|
ntfs_free(buf);
|
857 |
|
|
return -ENOTDIR;
|
858 |
|
|
}
|
859 |
|
|
entry = buf + 0x18 + NTFS_GETU16(buf + 0x18);
|
860 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 3: In index "
|
861 |
|
|
"allocation.\n", __FUNCTION__);
|
862 |
|
|
start = 0;
|
863 |
|
|
}
|
864 |
|
|
/* Process the entries. */
|
865 |
|
|
finish = *p_low;
|
866 |
|
|
for (; entry < (buf + ibs) && ntfs_entry_is_used(entry);
|
867 |
|
|
entry += NTFS_GETU16(entry + 8)) {
|
868 |
|
|
if (start < finish) {
|
869 |
|
|
/* Skip entries that were already processed. */
|
870 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 4: Skipping "
|
871 |
|
|
"already processed entry p_high 0x%x, "
|
872 |
|
|
"p_low 0x%x.\n", __FUNCTION__, *p_high,
|
873 |
|
|
start);
|
874 |
|
|
start++;
|
875 |
|
|
continue;
|
876 |
|
|
}
|
877 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 5: Processing entry "
|
878 |
|
|
"p_high 0x%x, p_low 0x%x.\n", __FUNCTION__,
|
879 |
|
|
*p_high, *p_low);
|
880 |
|
|
if ((err = cb(entry, param))) {
|
881 |
|
|
/* filldir signalled us to stop. */
|
882 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 6: cb returned "
|
883 |
|
|
"%i, returning 0, p_high 0x%x, "
|
884 |
|
|
"p_low 0x%x.\n", __FUNCTION__, err,
|
885 |
|
|
*p_high, *p_low);
|
886 |
|
|
ntfs_free(buf);
|
887 |
|
|
return 0;
|
888 |
|
|
}
|
889 |
|
|
++*p_low;
|
890 |
|
|
}
|
891 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 7: After processing entries, "
|
892 |
|
|
"p_high 0x%x, p_low 0x%x.\n", __FUNCTION__, *p_high,
|
893 |
|
|
*p_low);
|
894 |
|
|
/* We have to locate the next record. */
|
895 |
|
|
ntfs_free(buf);
|
896 |
|
|
buf = 0;
|
897 |
|
|
*p_low = 0;
|
898 |
|
|
attr = ntfs_find_attr(ino, vol->at_bitmap, I30);
|
899 |
|
|
if (!attr) {
|
900 |
|
|
/* Directory does not have index bitmap and index allocation. */
|
901 |
|
|
*p_high = 0x7fff;
|
902 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 8: No index allocation. "
|
903 |
|
|
"Returning 0, p_high 0x7fff, p_low 0x0.\n",
|
904 |
|
|
__FUNCTION__);
|
905 |
|
|
return 0;
|
906 |
|
|
}
|
907 |
|
|
max_size = attr->size;
|
908 |
|
|
if (max_size > 0x7fff >> 3) {
|
909 |
|
|
ntfs_error("%s(): Directory too large. Visible "
|
910 |
|
|
"length is truncated.\n", __FUNCTION__);
|
911 |
|
|
max_size = 0x7fff >> 3;
|
912 |
|
|
}
|
913 |
|
|
buf = ntfs_malloc(max_size);
|
914 |
|
|
if (!buf)
|
915 |
|
|
return -ENOMEM;
|
916 |
|
|
io.param = buf;
|
917 |
|
|
io.size = max_size;
|
918 |
|
|
err = ntfs_read_attr(ino, vol->at_bitmap, I30, 0, &io);
|
919 |
|
|
if (err || io.size != max_size)
|
920 |
|
|
goto read_err_ret;
|
921 |
|
|
attr = ntfs_find_attr(ino, vol->at_index_allocation, I30);
|
922 |
|
|
if (!attr) {
|
923 |
|
|
ntfs_free(buf);
|
924 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 9: Find attr failed. "
|
925 |
|
|
"Returning -EIO.\n", __FUNCTION__);
|
926 |
|
|
return -EIO;
|
927 |
|
|
}
|
928 |
|
|
if (attr->resident) {
|
929 |
|
|
ntfs_free(buf);
|
930 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 9.5: IA is resident. Not"
|
931 |
|
|
" allowed. Returning EINVAL.\n", __FUNCTION__);
|
932 |
|
|
return -EINVAL;
|
933 |
|
|
}
|
934 |
|
|
/* Loop while going through non-allocated index records. */
|
935 |
|
|
max_size <<= 3;
|
936 |
|
|
while (1) {
|
937 |
|
|
if (++*p_high >= 0x7fff) {
|
938 |
|
|
ntfs_error("%s(): Unsorted 10: Directory "
|
939 |
|
|
"inode 0x%lx overflowed the maximum "
|
940 |
|
|
"number of index allocation buffers "
|
941 |
|
|
"the driver can cope with. Pretending "
|
942 |
|
|
"to be at end of directory.\n",
|
943 |
|
|
__FUNCTION__, ino->i_number);
|
944 |
|
|
goto fake_eod;
|
945 |
|
|
}
|
946 |
|
|
if (*p_high > max_size || (s64)*p_high << ibs_bits >
|
947 |
|
|
attr->initialized) {
|
948 |
|
|
fake_eod:
|
949 |
|
|
/* No more index records. */
|
950 |
|
|
*p_high = 0x7fff;
|
951 |
|
|
*p_low = 0;
|
952 |
|
|
ntfs_free(buf);
|
953 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 10.5: No more "
|
954 |
|
|
"index records. Returning 0, p_high "
|
955 |
|
|
"0x7fff, p_low 0.\n", __FUNCTION__);
|
956 |
|
|
return 0;
|
957 |
|
|
}
|
958 |
|
|
byte = (ntfs_cluster_t)(*p_high - 1);
|
959 |
|
|
bit = 1 << (byte & 7);
|
960 |
|
|
byte >>= 3;
|
961 |
|
|
if ((buf[byte] & bit))
|
962 |
|
|
break;
|
963 |
|
|
};
|
964 |
|
|
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 11: Done. Returning 0, p_high "
|
965 |
|
|
"0x%x, p_low 0x%x.\n", __FUNCTION__, *p_high, *p_low);
|
966 |
|
|
ntfs_free(buf);
|
967 |
|
|
return 0;
|
968 |
|
|
read_err_ret:
|
969 |
|
|
if (!err)
|
970 |
|
|
err = -EIO;
|
971 |
|
|
ntfs_error("%s(): Read failed. Returning error code %i.\n",
|
972 |
|
|
__FUNCTION__, err);
|
973 |
|
|
ntfs_free(buf);
|
974 |
|
|
return err;
|
975 |
|
|
}
|
976 |
|
|
|
977 |
|
|
int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name)
|
978 |
|
|
{
|
979 |
|
|
ntfs_iterate_s walk;
|
980 |
|
|
int nsize, esize;
|
981 |
|
|
ntfs_u8* entry, *ndata;
|
982 |
|
|
int error;
|
983 |
|
|
|
984 |
|
|
walk.type = DIR_INSERT;
|
985 |
|
|
walk.dir = dir;
|
986 |
|
|
walk.u.flags = 0;
|
987 |
|
|
nsize = name->size;
|
988 |
|
|
ndata = name->d.data;
|
989 |
|
|
walk.name = (ntfs_u16*)(ndata + 0x42);
|
990 |
|
|
walk.namelen = NTFS_GETU8(ndata + 0x40);
|
991 |
|
|
walk.new_entry_size = esize = (nsize + 0x10 + 7) & ~7;
|
992 |
|
|
walk.new_entry = entry = ntfs_malloc(esize);
|
993 |
|
|
if (!entry)
|
994 |
|
|
return -ENOMEM;
|
995 |
|
|
NTFS_PUTINUM(entry, new);
|
996 |
|
|
NTFS_PUTU16(entry + 0x8, esize); /* Size of entry. */
|
997 |
|
|
NTFS_PUTU16(entry + 0xA, nsize); /* Size of original name attribute. */
|
998 |
|
|
NTFS_PUTU16(entry + 0xC, 0); /* Flags. */
|
999 |
|
|
NTFS_PUTU16(entry + 0xE, 0); /* Reserved. */
|
1000 |
|
|
ntfs_memcpy(entry + 0x10, ndata, nsize);
|
1001 |
|
|
ntfs_bzero(entry + 0x10 + nsize, esize - 0x10 - nsize);
|
1002 |
|
|
error = ntfs_getdir(&walk);
|
1003 |
|
|
if (walk.new_entry)
|
1004 |
|
|
ntfs_free(walk.new_entry);
|
1005 |
|
|
return error;
|
1006 |
|
|
}
|
1007 |
|
|
|
1008 |
|
|
#if 0
|
1009 |
|
|
int ntfs_dir_add1(ntfs_inode *dir, const char* name, int namelen,
|
1010 |
|
|
ntfs_inode *ino)
|
1011 |
|
|
{
|
1012 |
|
|
ntfs_iterate_s walk;
|
1013 |
|
|
int error;
|
1014 |
|
|
int nsize;
|
1015 |
|
|
char *entry;
|
1016 |
|
|
ntfs_attribute *name_attr;
|
1017 |
|
|
error = ntfs_decodeuni(dir->vol, name, namelen, &walk.name,
|
1018 |
|
|
&walk.namelen);
|
1019 |
|
|
if (error)
|
1020 |
|
|
return error;
|
1021 |
|
|
/* FIXME: Set flags. */
|
1022 |
|
|
walk.type = DIR_INSERT;
|
1023 |
|
|
walk.dir = dir;
|
1024 |
|
|
/* walk.new = ino; */
|
1025 |
|
|
/* Prepare new entry. */
|
1026 |
|
|
/* Round up to a multiple of 8. */
|
1027 |
|
|
walk.new_entry_size = nsize = ((0x52 + 2 * walk.namelen + 7) / 8) * 8;
|
1028 |
|
|
walk.new_entry = entry = ntfs_malloc(nsize);
|
1029 |
|
|
if (!entry)
|
1030 |
|
|
return -ENOMEM;
|
1031 |
|
|
ntfs_bzero(entry, nsize);
|
1032 |
|
|
NTFS_PUTINUM(entry, ino);
|
1033 |
|
|
NTFS_PUTU16(entry + 8, nsize);
|
1034 |
|
|
NTFS_PUTU16(entry + 0xA, 0x42 + 2 * namelen); /* FIXME: Size of name
|
1035 |
|
|
* attribute. */
|
1036 |
|
|
NTFS_PUTU32(entry + 0xC, 0); /* FIXME: D-F? */
|
1037 |
|
|
name_attr = ntfs_find_attr(ino, vol->at_file_name, 0);
|
1038 |
|
|
/* FIXME: multiple names */
|
1039 |
|
|
if (!name_attr || !name_attr->resident)
|
1040 |
|
|
return -EIDRM;
|
1041 |
|
|
/* Directory, file stamps, sizes, filename. */
|
1042 |
|
|
ntfs_memcpy(entry + 0x10, name_attr->d.data, 0x42 + 2 * namelen);
|
1043 |
|
|
error = ntfs_getdir(&walk);
|
1044 |
|
|
ntfs_free(walk.name);
|
1045 |
|
|
return error;
|
1046 |
|
|
}
|
1047 |
|
|
#endif
|
1048 |
|
|
|
1049 |
|
|
/* Fills out and creates an INDEX_ROOT attribute. */
|
1050 |
|
|
int ntfs_add_index_root(ntfs_inode *ino, int type)
|
1051 |
|
|
{
|
1052 |
|
|
ntfs_attribute *da;
|
1053 |
|
|
ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry. */
|
1054 |
|
|
char name[10];
|
1055 |
|
|
|
1056 |
|
|
NTFS_PUTU32(data, type);
|
1057 |
|
|
/* Collation rule. 1 == COLLATION_FILENAME */
|
1058 |
|
|
NTFS_PUTU32(data + 4, 1);
|
1059 |
|
|
NTFS_PUTU32(data + 8, ino->vol->index_record_size);
|
1060 |
|
|
NTFS_PUTU32(data + 0xC, ino->vol->index_clusters_per_record);
|
1061 |
|
|
/* Byte offset to first INDEX_ENTRY. */
|
1062 |
|
|
NTFS_PUTU32(data + 0x10, 0x10);
|
1063 |
|
|
/* Size of entries, including header. */
|
1064 |
|
|
NTFS_PUTU32(data + 0x14, 0x20);
|
1065 |
|
|
NTFS_PUTU32(data + 0x18, 0x20);
|
1066 |
|
|
/* No index allocation, yet. */
|
1067 |
|
|
NTFS_PUTU32(data + 0x1C, 0);
|
1068 |
|
|
/* Add last entry. */
|
1069 |
|
|
/* Indexed MFT record. */
|
1070 |
|
|
NTFS_PUTU64(data + 0x20, 0);
|
1071 |
|
|
/* Size of entry. */
|
1072 |
|
|
NTFS_PUTU32(data + 0x28, 0x10);
|
1073 |
|
|
/* Flags: Last entry, no child nodes. */
|
1074 |
|
|
NTFS_PUTU32(data + 0x2C, 2);
|
1075 |
|
|
/* Compute name. */
|
1076 |
|
|
ntfs_indexname(name, type);
|
1077 |
|
|
return ntfs_create_attr(ino, ino->vol->at_index_root, name,
|
1078 |
|
|
data, sizeof(data), &da);
|
1079 |
|
|
}
|
1080 |
|
|
|
1081 |
|
|
int ntfs_mkdir(ntfs_inode *dir, const char *name, int namelen,
|
1082 |
|
|
ntfs_inode *result)
|
1083 |
|
|
{
|
1084 |
|
|
int error;
|
1085 |
|
|
|
1086 |
|
|
error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR);
|
1087 |
|
|
if (error)
|
1088 |
|
|
goto out;
|
1089 |
|
|
error = ntfs_add_index_root(result, 0x30);
|
1090 |
|
|
if (error)
|
1091 |
|
|
goto out;
|
1092 |
|
|
/* Set directory bit. */
|
1093 |
|
|
result->attr[0x16] |= 2;
|
1094 |
|
|
error = ntfs_update_inode(dir);
|
1095 |
|
|
if (error)
|
1096 |
|
|
goto out;
|
1097 |
|
|
error = ntfs_update_inode(result);
|
1098 |
|
|
if (error)
|
1099 |
|
|
goto out;
|
1100 |
|
|
out:
|
1101 |
|
|
return error;
|
1102 |
|
|
}
|
1103 |
|
|
|