[UBTRFS] Update to 1.0.1
[reactos.git] / sdk / lib / fslib / ext2lib / Inode.c
1 /*
2 * PROJECT: Mke2fs
3 * FILE: Inode.c
4 * PROGRAMMER: Matt Wu <mattwu@163.com>
5 * HOMEPAGE: http://ext2.yeah.net
6 */
7
8 /* INCLUDES **************************************************************/
9
10 #include "Mke2fs.h"
11 #include <debug.h>
12
13 /* DEFINITIONS ***********************************************************/
14
15 extern char *device_name;
16
17 /* FUNCTIONS *************************************************************/
18
19
20 bool ext2_get_inode_lba(PEXT2_FILESYS Ext2Sys, ULONG no, LONGLONG *offset)
21 {
22 LONGLONG loc = 0;
23 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
24
25 if (no < 1 || no > pExt2Sb->s_inodes_count)
26 {
27 DPRINT1("Mke2fs: Inode value %lu was out of range in load_inode.(1-%ld)\n",
28 no, pExt2Sb->s_inodes_count);
29 *offset = 0;
30 return false;
31 }
32
33 loc = (LONGLONG)(Ext2Sys->blocksize) * Ext2Sys->group_desc[(no - 1) / pExt2Sb->s_inodes_per_group].bg_inode_table +
34 ((no - 1) % pExt2Sb->s_inodes_per_group) * sizeof(EXT2_INODE);
35
36 *offset = loc;
37
38 return true;
39 }
40
41 bool ext2_load_inode(PEXT2_FILESYS Ext2Sys, ULONG no, PEXT2_INODE pInode)
42 {
43 LONGLONG Offset;
44 bool bRet = false;
45
46 if (ext2_get_inode_lba(Ext2Sys, no, &Offset))
47 {
48 bRet = NT_SUCCESS(Ext2ReadDisk(
49 Ext2Sys,
50 Offset,
51 sizeof(EXT2_INODE),
52 (unsigned char *)pInode));
53 }
54
55 return bRet;
56 }
57
58
59 bool ext2_save_inode(PEXT2_FILESYS Ext2Sys, ULONG no, PEXT2_INODE pInode)
60 {
61 LONGLONG offset;
62 bool bRet = false;
63
64 if (ext2_get_inode_lba(Ext2Sys, no, &offset))
65 {
66 bRet = NT_SUCCESS(Ext2WriteDisk(
67 Ext2Sys,
68 offset,
69 sizeof(EXT2_INODE),
70 (unsigned char *)pInode));
71 }
72
73 return bRet;
74 }
75
76
77 /*
78 * Right now, just search forward from the parent directory's block
79 * group to find the next free inode.
80 *
81 * Should have a special policy for directories.
82 */
83
84 bool ext2_new_inode(PEXT2_FILESYS fs, ULONG dir, int mode,
85 PEXT2_INODE_BITMAP map, ULONG *ret)
86 {
87 ULONG dir_group = 0;
88 ULONG i;
89 ULONG start_inode;
90
91 if (!map)
92 map = fs->inode_map;
93
94 if (!map)
95 return false;
96
97 if (dir > 0)
98 dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->ext2_sb);
99
100 start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->ext2_sb)) + 1;
101
102 if (start_inode < EXT2_FIRST_INODE(fs->ext2_sb))
103 start_inode = EXT2_FIRST_INODE(fs->ext2_sb);
104
105 i = start_inode;
106
107 do
108 {
109 if (!ext2_test_inode_bitmap(map, i))
110 break;
111
112 i++;
113
114 if (i > fs->ext2_sb->s_inodes_count)
115 i = EXT2_FIRST_INODE(fs->ext2_sb);
116
117 } while (i != start_inode);
118
119 if (ext2_test_inode_bitmap(map, i))
120 return false;
121
122 *ret = i;
123
124 return true;
125 }
126
127
128 bool ext2_expand_block( PEXT2_FILESYS Ext2Sys, PEXT2_INODE Inode,
129 ULONG dwContent, ULONG Index, int layer,
130 ULONG newBlk, ULONG *dwRet, ULONG *off )
131 {
132 ULONG *pData = NULL;
133 ULONG i = 0, j = 0, temp = 1;
134 ULONG dwBlk;
135 ULONG dwNewBlk = newBlk;
136 bool bDirty = false;
137 bool bRet = true;
138 ULONG Offset = 0;
139
140 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
141
142 pData = (ULONG *)RtlAllocateHeap(RtlGetProcessHeap(), 0, Ext2Sys->blocksize);
143
144 if (!pData)
145 {
146 bRet = false;
147 goto errorout;
148 }
149
150 if (!ext2_read_block(Ext2Sys, dwContent, (void *)pData))
151 {
152 bRet = false;
153 goto errorout;
154 }
155
156 if (layer == 1)
157 {
158 *dwRet = dwContent;
159 *off = Index;
160 pData[Index] = newBlk;
161
162 bDirty = TRUE;
163 }
164 else if (layer <= 3)
165 {
166 temp = 1 << ((10 + pExt2Sb->s_log_block_size - 2) * (layer - 1));
167
168 i = Index / temp;
169 j = Index % temp;
170
171 dwBlk = pData[i];
172
173 if (dwBlk == 0)
174 {
175 if (ext2_alloc_block(Ext2Sys, 0, &dwBlk) )
176 {
177 pData[i] = dwBlk;
178 bDirty = true;
179
180 Inode->i_blocks += (Ext2Sys->blocksize / SECTOR_SIZE);
181 }
182
183 if (!bDirty)
184 goto errorout;
185 }
186
187 if (!ext2_expand_block(Ext2Sys, Inode, dwBlk, j, layer - 1, bDirty, &dwNewBlk, &Offset))
188 {
189 bRet = false;
190 DPRINT1("Mke2fs: ext2_expand_block: ... error recuise...\n");
191 goto errorout;
192 }
193 }
194
195 if (bDirty)
196 {
197 bRet = ext2_write_block(Ext2Sys, dwContent, (void *)pData);
198 }
199
200
201 errorout:
202
203 if (pData)
204 RtlFreeHeap(RtlGetProcessHeap(), 0, pData);
205
206 if (bRet && dwRet)
207 *dwRet = dwNewBlk;
208
209 return bRet;
210 }
211
212 bool ext2_expand_inode( PEXT2_FILESYS Ext2Sys,
213 PEXT2_INODE Inode,
214 ULONG newBlk )
215 {
216 ULONG dwSizes[4] = {12, 1, 1, 1};
217 ULONG Index = 0;
218 ULONG dwTotal = 0;
219 ULONG dwBlk = 0, dwNewBlk = 0, Offset = 0;
220 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
221 int i = 0;
222 bool bRet = true;
223 ULONG TotalBlocks;
224
225 TotalBlocks = Inode->i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE);
226 Index = Ext2DataBlocks(Ext2Sys, TotalBlocks);
227
228 for (i = 0; i < 4; i++)
229 {
230 dwSizes[i] = dwSizes[i] << ((10 + pExt2Sb->s_log_block_size - 2) * i);
231 dwTotal += dwSizes[i];
232 }
233
234 if (Index >= dwTotal)
235 {
236 DPRINT1("Mke2fs: ext2_expand_inode: beyond the maxinum size of an inode.\n");
237 return false;
238 }
239
240 for (i = 0; i < 4; i++)
241 {
242 if (Index < dwSizes[i])
243 {
244 if (i == 0)
245 {
246 Inode->i_block[Index] = newBlk;
247 }
248 else
249 {
250 dwBlk = Inode->i_block[(i + 12 - 1)];
251
252 if (dwBlk == 0)
253 {
254 if (ext2_alloc_block(Ext2Sys, 0, &dwBlk))
255 {
256 Inode->i_block[(i + 12 - 1)] = dwBlk;
257
258 Inode->i_blocks += (Ext2Sys->blocksize / SECTOR_SIZE);
259 }
260 else
261 {
262 break;
263 }
264 }
265
266 dwNewBlk = 0;
267 bRet = ext2_expand_block(
268 Ext2Sys,
269 Inode,
270 dwBlk,
271 Index,
272 i,
273 newBlk,
274 &dwNewBlk,
275 &Offset );
276 }
277
278 break;
279 }
280
281 Index -= dwSizes[i];
282 }
283
284 return bRet;
285 }
286
287
288 bool ext2_get_block(PEXT2_FILESYS Ext2Sys, ULONG dwContent, ULONG Index, int layer, ULONG *dwRet)
289 {
290 ULONG *pData = NULL;
291 LONGLONG Offset = 0;
292 ULONG i = 0, j = 0, temp = 1;
293 ULONG dwBlk = 0;
294 bool bRet = true;
295
296 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
297
298 Offset = (LONGLONG) dwContent;
299 Offset = Offset * Ext2Sys->blocksize;
300
301 pData = (ULONG *)RtlAllocateHeap(RtlGetProcessHeap(), 0, Ext2Sys->blocksize);
302
303 if (!pData)
304 {
305 return false;
306 }
307 memset(pData, 0, Ext2Sys->blocksize);
308
309 if (layer == 0)
310 {
311 dwBlk = dwContent;
312 }
313 else if (layer <= 3)
314 {
315
316 if (!ext2_read_block(Ext2Sys, dwContent, (void *)pData))
317 {
318 bRet = false;
319 goto errorout;
320 }
321
322 temp = 1 << ((10 + pExt2Sb->s_log_block_size - 2) * (layer - 1));
323
324 i = Index / temp;
325 j = Index % temp;
326
327 if (!ext2_get_block(Ext2Sys, pData[i], j, layer - 1, &dwBlk))
328 {
329 bRet = false;
330 DPRINT1("Mke2fs: ext2_get_block: ... error recuise...\n");
331 goto errorout;
332 }
333 }
334
335 errorout:
336
337 if (pData)
338 RtlFreeHeap(RtlGetProcessHeap(), 0, pData);
339
340 if (bRet && dwRet)
341 *dwRet = dwBlk;
342
343 return bRet;
344 }
345
346 bool ext2_block_map(PEXT2_FILESYS Ext2Sys, PEXT2_INODE inode, ULONG block, ULONG *dwRet)
347 {
348 ULONG dwSizes[4] = { 12, 1, 1, 1 };
349 ULONG Index = 0;
350 ULONG dwBlk = 0;
351 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
352 UINT i;
353 bool bRet = false;
354
355 Index = block;
356
357 for (i = 0; i < 4; i++)
358 {
359 dwSizes[i] = dwSizes[i] << ((10 + pExt2Sb->s_log_block_size - 2) * i);
360 }
361
362 if (Index >= inode->i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE))
363 {
364 DPRINT1("Mke2fs: ext2_block_map: beyond the size of the inode.\n");
365 return false;
366 }
367
368 for (i = 0; i < 4; i++)
369 {
370 if (Index < dwSizes[i])
371 {
372 dwBlk = inode->i_block[i==0 ? (Index):(i + 12 - 1)];
373
374 bRet = ext2_get_block(Ext2Sys, dwBlk, Index , i, &dwBlk);
375
376 break;
377 }
378 Index -= dwSizes[i];
379 }
380
381 if (bRet && dwBlk)
382 {
383 if (dwRet)
384 *dwRet = dwBlk;
385
386 return true;
387 }
388 else
389 return false;
390 }
391
392 ULONG ext2_build_bdl(PEXT2_FILESYS Ext2Sys,
393 PEXT2_INODE ext2_inode,
394 IN ULONG offset,
395 IN ULONG size,
396 OUT PEXT2_BDL *ext2_bdl )
397 {
398 ULONG nBeg, nEnd, nBlocks;
399 ULONG dwBlk;
400 ULONG i;
401 ULONG dwBytes = 0;
402 LONGLONG lba;
403
404 PEXT2_BDL ext2bdl = NULL;
405
406 *ext2_bdl = NULL;
407
408 if (offset >= ext2_inode->i_size)
409 {
410 DPRINT1("Mke2fs: ext2_build_bdl: beyond the file range.\n");
411 return 0;
412 }
413
414 /*
415 if (offset + size > ext2_inode->i_size)
416 {
417 size = ext2_inode->i_size - offset;
418 }
419 */
420
421 nBeg = offset / Ext2Sys->blocksize;
422 nEnd = (size + offset + Ext2Sys->blocksize - 1) / Ext2Sys->blocksize;
423
424 nBlocks = nEnd - nBeg;
425
426 if (nBlocks > 0)
427 {
428 ext2bdl = (PEXT2_BDL)
429 RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(EXT2_BDL) * nBlocks);
430
431 if (ext2bdl)
432 {
433
434 memset(ext2bdl, 0, sizeof(EXT2_BDL) * nBlocks);
435
436 for (i = nBeg; i < nEnd; i++)
437 {
438 if (!ext2_block_map(Ext2Sys, ext2_inode, i, &dwBlk))
439 {
440 goto fail;
441 }
442
443 lba = (LONGLONG) dwBlk;
444 lba = lba * Ext2Sys->blocksize;
445
446 if (nBlocks == 1) // ie. (nBeg == nEnd - 1)
447 {
448 dwBytes = size;
449 ext2bdl[i - nBeg].Lba = lba + (LONGLONG)(offset % (Ext2Sys->blocksize));
450 ext2bdl[i - nBeg].Length = dwBytes;
451 ext2bdl[i - nBeg].Offset = 0;
452 }
453 else
454 {
455 if (i == nBeg)
456 {
457 dwBytes = Ext2Sys->blocksize - (offset % (Ext2Sys->blocksize));
458 ext2bdl[i - nBeg].Lba = lba + (LONGLONG)(offset % (Ext2Sys->blocksize));
459 ext2bdl[i - nBeg].Length = dwBytes;
460 ext2bdl[i - nBeg].Offset = 0;
461 }
462 else if (i == nEnd - 1)
463 {
464 ext2bdl[i - nBeg].Lba = lba;
465 ext2bdl[i - nBeg].Length = size - dwBytes;
466 ext2bdl[i - nBeg].Offset = dwBytes;
467 dwBytes = size;
468 }
469 else
470 {
471 ext2bdl[i - nBeg].Lba = lba;
472 ext2bdl[i - nBeg].Length = Ext2Sys->blocksize;
473 ext2bdl[i - nBeg].Offset = dwBytes;
474 dwBytes += Ext2Sys->blocksize;
475 }
476 }
477 }
478
479 *ext2_bdl = ext2bdl;
480 return nBlocks;
481 }
482 }
483
484 fail:
485
486 if (ext2bdl)
487 RtlFreeHeap(RtlGetProcessHeap(), 0, ext2bdl);
488
489 // Error
490 return 0;
491 }
492
493
494 bool ext2_read_inode(PEXT2_FILESYS Ext2Sys,
495 ULONG ino,
496 ULONG offset,
497 PVOID Buffer,
498 ULONG size,
499 PULONG dwReturn)
500 {
501 PEXT2_BDL ext2_bdl = NULL;
502 ULONG blocks, i;
503 bool bRet = true;
504 EXT2_INODE ext2_inode;
505 ULONG dwTotal = 0;
506
507 if (!ext2_load_inode(Ext2Sys, ino, &ext2_inode))
508 {
509 return false;
510 }
511
512 blocks = ext2_build_bdl(Ext2Sys, &ext2_inode, offset, size, &ext2_bdl);
513
514 if (blocks <= 0)
515 return false;
516
517
518 for(i = 0; i < blocks; i++)
519 {
520 bRet = NT_SUCCESS(Ext2ReadDisk(
521 Ext2Sys,
522 ext2_bdl[i].Lba,
523 ext2_bdl[i].Length,
524 (PUCHAR)Buffer + ext2_bdl[i].Offset
525 ));
526
527 if (!bRet)
528 break;
529 dwTotal += ext2_bdl[i].Length;
530 }
531
532 *dwReturn = dwTotal;
533
534 if (ext2_bdl)
535 RtlFreeHeap(RtlGetProcessHeap(), 0, ext2_bdl);
536
537 return bRet;
538 }
539
540
541 bool ext2_write_inode (PEXT2_FILESYS Ext2Sys,
542 ULONG ino,
543 ULONG offset,
544 PVOID Buffer,
545 ULONG size,
546 PULONG dwReturn )
547 {
548 PEXT2_BDL ext2_bdl = NULL;
549 ULONG blocks, i;
550 bool bRet = true;
551 EXT2_INODE inode;
552 ULONG dwTotal = 0;
553 ULONG dwBlk = 0;
554 ULONG TotalBlks;
555
556 blocks = (size + offset + Ext2Sys->blocksize - 1) / Ext2Sys->blocksize;
557
558 if (!ext2_load_inode(Ext2Sys, ino, &inode))
559 {
560 return false;
561 }
562
563 TotalBlks = inode.i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE);
564 TotalBlks = Ext2DataBlocks(Ext2Sys, TotalBlks);
565
566 if (blocks > TotalBlks)
567 {
568 for (i=0; i < (blocks - TotalBlks); i++)
569 {
570 if (ext2_alloc_block(Ext2Sys, 0, &dwBlk) )
571 {
572 ext2_expand_inode(Ext2Sys, &inode, dwBlk);
573 inode.i_blocks += (Ext2Sys->blocksize/SECTOR_SIZE);
574 }
575 }
576 }
577
578 blocks = ext2_build_bdl(Ext2Sys, &inode, offset, size, &ext2_bdl);
579
580 if (blocks <= 0)
581 return false;
582
583 for(i = 0; i < blocks; i++)
584 {
585 bRet = NT_SUCCESS(Ext2WriteDisk(
586 Ext2Sys,
587 ext2_bdl[i].Lba,
588 ext2_bdl[i].Length,
589 (PUCHAR)Buffer + ext2_bdl[i].Offset
590 ));
591
592 if (!bRet)
593 {
594 goto errorout;
595 }
596
597 dwTotal += ext2_bdl[i].Length;
598 }
599
600 *dwReturn = dwTotal;
601
602 if (size + offset > inode.i_size)
603 {
604 inode.i_size = size + offset;
605 }
606
607 ext2_save_inode(Ext2Sys, ino, &inode);
608
609
610 errorout:
611
612 if (ext2_bdl)
613 RtlFreeHeap(RtlGetProcessHeap(), 0, ext2_bdl);
614
615 return bRet;
616 }
617
618 bool
619 ext2_add_entry( PEXT2_FILESYS Ext2Sys,
620 ULONG parent, ULONG inode,
621 int filetype, char *name )
622 {
623 PEXT2_DIR_ENTRY2 dir = NULL, newdir = NULL;
624 EXT2_INODE parent_inode;
625 ULONG dwRet;
626 char *buf;
627 int rec_len;
628 bool bRet = false;
629
630 rec_len = EXT2_DIR_REC_LEN(strlen(name));
631
632 if (!ext2_load_inode(Ext2Sys, parent, &parent_inode))
633 {
634 return false;
635 }
636
637 buf = (char *)RtlAllocateHeap(RtlGetProcessHeap(), 0, parent_inode.i_size);
638
639 if (!ext2_read_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet))
640 {
641 return false;
642 }
643
644 dir = (PEXT2_DIR_ENTRY2) buf;
645
646 while ((char *)dir < buf + parent_inode.i_size)
647 {
648 if ((dir->inode == 0 && dir->rec_len >= rec_len) ||
649 (dir->rec_len >= dir->name_len + rec_len) )
650 {
651 if (dir->inode)
652 {
653 newdir = (PEXT2_DIR_ENTRY2) ((PUCHAR)dir + EXT2_DIR_REC_LEN(dir->name_len));
654 newdir->rec_len = dir->rec_len - EXT2_DIR_REC_LEN(dir->name_len);
655
656 dir->rec_len = EXT2_DIR_REC_LEN(dir->name_len);
657
658 dir = newdir;
659 }
660
661 dir->file_type = filetype;
662 dir->inode = inode;
663 dir->name_len = strlen(name);
664 memcpy(dir->name, name, strlen(name));
665
666 bRet = true;
667 break;
668 }
669
670 dir = (PEXT2_DIR_ENTRY2) (dir->rec_len + (PUCHAR) dir);
671 }
672
673
674 if (bRet)
675 return ext2_write_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet);
676
677 return bRet;
678 }
679
680
681 bool ext2_reserve_inodes(PEXT2_FILESYS fs)
682 {
683 ULONG i;
684 int group;
685
686 for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->ext2_sb); i++)
687 {
688 ext2_mark_inode_bitmap(fs->inode_map, i);
689 group = ext2_group_of_ino(fs, i);
690 fs->group_desc[group].bg_free_inodes_count--;
691 fs->ext2_sb->s_free_inodes_count--;
692 }
693
694 return true;
695 }