xref: /reactos/sdk/lib/fslib/ext2lib/Inode.c (revision cdf90707)
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 }
696