2a7bc2fe5709bbe719e0027bf7441d86cba11841
[tpg/acess2.git] / KernelLand / Modules / Filesystems / Ext2 / dir.c
1 /*
2  * Acess2 Ext2 Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * dir.c
6  * - Directory Handling
7  */
8 #define DEBUG   0
9 #define VERBOSE 0
10 #include "ext2_common.h"
11
12 // === MACROS ===
13 #define BLOCK_DIR_OFS(_data, _block)    ((Uint16*)(_data)[(_block)])
14
15 // === PROTOTYPES ===
16 char    *Ext2_ReadDir(tVFS_Node *Node, int Pos);
17 tVFS_Node       *Ext2_FindDir(tVFS_Node *Node, const char *FileName);
18  int    Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
19  int    Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
20  int    Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name);
21 // --- Helpers ---
22 tVFS_Node       *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId);
23
24 // === GLOBALS ===
25 tVFS_NodeType   gExt2_DirType = {
26         .TypeName = "ext2-dir",
27         .ReadDir = Ext2_ReadDir,
28         .FindDir = Ext2_FindDir,
29         .MkNod = Ext2_MkNod,
30         .Relink = Ext2_Relink,
31         .Link = Ext2_Link,
32         .Close = Ext2_CloseFile
33         };
34 tVFS_NodeType   gExt2_FileType = {
35         .TypeName = "ext2-file",
36         .Read = Ext2_Read,
37         .Write = Ext2_Write,
38         .Close = Ext2_CloseFile
39         };
40
41 // === CODE ===
42 /**
43  * \brief Reads a directory entry
44  * \param Node  Directory node
45  * \param Pos   Position of desired element
46  */
47 char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
48 {
49         tExt2_Inode     inode;
50         tExt2_DirEnt    dirent;
51         Uint64  Base;   // Block's Base Address
52          int    block = 0;
53         Uint    ofs = 0;
54          int    entNum = 0;
55         tExt2_Disk      *disk = Node->ImplPtr;
56         Uint    size;
57         
58         ENTER("pNode iPos", Node, Pos);
59         
60         // Read directory's inode
61         Ext2_int_ReadInode(disk, Node->Inode, &inode);
62         size = inode.i_size;
63         
64         LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
65         
66         // Find Entry
67         // Get First Block
68         // - Do this ourselves as it is a simple operation
69         Base = inode.i_block[0] * disk->BlockSize;
70         // Scan directory
71         while(Pos -- && size > 0)
72         {
73                 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
74                 ofs += dirent.rec_len;
75                 size -= dirent.rec_len;
76                 entNum ++;
77                 
78                 if(ofs >= disk->BlockSize) {
79                         block ++;
80                         if( ofs > disk->BlockSize ) {
81                                 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
82                                         entNum-1, Node->Inode);
83                         }
84                         ofs = 0;
85                         Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
86                 }
87         }
88         
89         // Check for the end of the list
90         if(size <= 0) {
91                 LEAVE('n');
92                 return NULL;
93         }
94         
95         // Read Entry
96         VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
97         //LOG("dirent.inode = %i", dirent.inode);
98         //LOG("dirent.rec_len = %i", dirent.rec_len);
99         //LOG("dirent.name_len = %i", dirent.name_len);
100         dirent.name[ dirent.name_len ] = '\0';  // Cap off string
101         
102         
103         // Ignore . and .. (these are done in the VFS)
104         if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
105         ||  (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
106                 LEAVE('p', VFS_SKIP);
107                 return VFS_SKIP;        // Skip
108         }
109         
110         LEAVE('s', dirent.name);
111         // Create new node
112         return strdup(dirent.name);
113 }
114
115 /**
116  * \brief Gets information about a file
117  * \param Node  Parent Node
118  * \param Filename      Name of wanted file
119  * \return VFS Node of file
120  */
121 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
122 {
123         tExt2_Disk      *disk = Node->ImplPtr;
124         tExt2_Inode     inode;
125         tExt2_DirEnt    dirent;
126         Uint64  Base;   // Block's Base Address
127          int    block = 0;
128         Uint    ofs = 0;
129          int    entNum = 0;
130         Uint    size;
131          int    filenameLen = strlen(Filename);
132         
133         // Read directory's inode
134         Ext2_int_ReadInode(disk, Node->Inode, &inode);
135         size = inode.i_size;
136         
137         // Get First Block
138         // - Do this ourselves as it is a simple operation
139         Base = inode.i_block[0] * disk->BlockSize;
140         // Find File
141         while(size > 0)
142         {
143                 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
144                 // TODO: Possible overrun if name_len == 255?
145                 dirent.name[ dirent.name_len ] = '\0';  // Cap off string
146                 // If it matches, create a node and return it
147                 if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
148                         return Ext2_int_CreateNode( disk, dirent.inode );
149                 // Increment pointers
150                 ofs += dirent.rec_len;
151                 size -= dirent.rec_len;
152                 entNum ++;
153                 
154                 // Check for end of block
155                 if(ofs >= disk->BlockSize) {
156                         block ++;
157                         if( ofs > disk->BlockSize ) {
158                                 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
159                                         entNum-1, Node->Inode);
160                         }
161                         ofs = 0;
162                         Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
163                 }
164         }
165         
166         return NULL;
167 }
168
169 /**
170  * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
171  * \brief Create a new node
172  */
173 int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
174 {
175         #if 0
176         tVFS_Node       *child;
177         Uint64  inodeNum;
178         tExt2_Inode     inode;
179         inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
180         
181         memset(&inode, 0, sizeof(tExt2_Inode));
182         
183         // File type
184         inode.i_mode = 0664;
185         if( Flags & VFS_FFLAG_READONLY )
186                 inode.i_mode &= ~0222;
187         if( Flags & VFS_FFLAG_SYMLINK )
188                 inode.i_mode |= EXT2_S_IFLNK;
189         else if( Flags & VFS_FFLAG_DIRECTORY )
190                 inode.i_mode |= EXT2_S_IFDIR | 0111;
191         
192         inode.i_uid = Threads_GetUID();
193         inode.i_gid = Threads_GetGID();
194         inode.i_ctime =
195                 inode.i_mtime =
196                 inode.i_atime = now() / 1000;
197         
198         child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
199         return Ext2_Link(Parent, child, Name);
200         #else
201         return 1;
202         #endif
203 }
204
205 /**
206  * \brief Rename a file
207  * \param Node  This (directory) node
208  * \param OldName       Old name of file
209  * \param NewName       New name for file
210  * \return Boolean Failure - See ::tVFS_Node.Relink for info
211  */
212 int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
213 {
214         return 1;
215 }
216
217 /**
218  * \brief Links an existing node to a new name
219  * \param Parent        Parent (directory) node
220  * \param Node  Node to link
221  * \param Name  New name for the node
222  * \return Boolean Failure - See ::tVFS_Node.Link for info
223  */
224 int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
225 {       
226         #if 0
227         tExt2_Disk      *disk = Node->ImplPtr;
228         tExt2_Inode     inode;
229         tExt2_DirEnt    dirent;
230         tExt2_DirEnt    newEntry;
231         Uint64  Base;   // Block's Base Address
232          int    block = 0, ofs = 0;
233         Uint    size;
234         void    *blockData;
235          int    bestMatch = -1, bestSize, bestBlock, bestOfs;
236          int    nEntries;
237         
238         blockData = malloc(disk->BlockSize);
239         
240         // Read child inode (get's the file type)
241         Ext2_int_ReadInode(disk, Child->Inode, &inode);
242         
243         // Create a stub entry
244         newEntry.inode = Child->Inode;
245         newEntry.name_len = strlen(Name);
246         newEntry.rec_len = (newEntry.name_len+3+8)&~3;
247         newEntry.type = inode.i_mode >> 12;
248         memcpy(newEntry.name, Name, newEntry.name_len);
249         
250         // Read directory's inode
251         Ext2_int_ReadInode(disk, Node->Inode, &inode);
252         size = inode.i_size;
253         
254         // Get a lock on the inode
255         Ext2_int_LockInode(disk, Node->Inode);
256         
257         // Get First Block
258         // - Do this ourselves as it is a simple operation
259         base = inode.i_block[0] * disk->BlockSize;
260         VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
261         block = 0;
262         // Find File
263         while(size > 0)
264         {
265                 dirent = blockData + ofs;
266                 // Sanity Check the entry
267                 if(ofs + dirent->rec_len > disk->BlockSize) {
268                         Log_Warning("EXT2",
269                                 "Directory entry %i of inode 0x%x extends over a block boundary",
270                                 nEntries, (Uint)Node->Inode);
271                 }
272                 else {
273                 
274                         // Free entry
275                         if(dirent->type == 0) {
276                                 if( dirent->rec_len >= newEntry.rec_len
277                                  && (bestMatch == -1 || bestSize > dirent->rec_len) )
278                                 {
279                                         bestMatch = nEntries;
280                                         bestSize = dirent->rec_len;
281                                         bestBlock = block;
282                                         bestOfs = ofs;
283                                 }
284                         }
285                         // Non free - check name to avoid duplicates
286                         else {
287                                 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
288                                         Ext2_int_UnlockInode(disk, Node->Inode);
289                                         return 1;       // ERR_???
290                                 }
291                         }
292                 }
293                 
294                 // Increment the pointer
295                 nEntries ++;
296                 ofs += dirent->rec_len;
297                 if( ofs >= disk->BlockSize ) {
298                         // Read the next block if needed
299                         BLOCK_DIR_OFS(Node->Data, block) = nEntries;
300                         block ++;
301                         ofs = 0;
302                         base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
303                         VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
304                 }
305         }
306         
307         // Check if a free slot was found
308         if( bestMatch >= 0 ) {
309                 // Read-Modify-Write
310                 bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
311                 if( block > 0 )
312                         bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
313                 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
314                 dirent = blockData + bestOfs;
315                 memcpy(dirent, newEntry, newEntry.rec_len);
316                 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
317         }
318         else {
319                 // Allocate block, Write
320                 block = Ext2_int_AllocateBlock(Disk, block);
321                 Log_Warning("EXT2", "");
322         }
323
324         Ext2_int_UnlockInode(disk, Node->Inode);
325         return 0;
326         #else
327         return 1;
328         #endif
329 }
330
331 // ---- INTERNAL FUNCTIONS ----
332 /**
333  * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
334  * \brief Create a new VFS Node
335  */
336 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
337 {
338         tExt2_Inode     inode;
339         tVFS_Node       retNode;
340         tVFS_Node       *tmpNode;
341         
342         if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
343                 return NULL;
344         
345         if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
346                 return tmpNode;
347         
348         
349         // Set identifiers
350         retNode.Inode = InodeID;
351         retNode.ImplPtr = Disk;
352         
353         // Set file length
354         retNode.Size = inode.i_size;
355         retNode.Data = NULL;
356         
357         // Set Access Permissions
358         retNode.UID = inode.i_uid;
359         retNode.GID = inode.i_gid;
360         retNode.NumACLs = 3;
361         retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
362         
363         //  Set Function Pointers
364         retNode.Type = &gExt2_FileType;
365         
366         switch(inode.i_mode & EXT2_S_IFMT)
367         {
368         // Symbolic Link
369         case EXT2_S_IFLNK:
370                 retNode.Flags = VFS_FFLAG_SYMLINK;
371                 break;
372         // Regular File
373         case EXT2_S_IFREG:
374                 retNode.Flags = 0;
375                 retNode.Size |= (Uint64)inode.i_dir_acl << 32;
376                 break;
377         // Directory
378         case EXT2_S_IFDIR:
379                 retNode.Type = &gExt2_DirType;
380                 retNode.Flags = VFS_FFLAG_DIRECTORY;
381                 retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
382                 break;
383         // Unknown, Write protect it to be safe 
384         default:
385                 retNode.Flags = VFS_FFLAG_READONLY;
386                 break;
387         }
388         
389         // Set Timestamps
390         retNode.ATime = inode.i_atime * 1000;
391         retNode.MTime = inode.i_mtime * 1000;
392         retNode.CTime = inode.i_ctime * 1000;
393         
394         // Save in node cache and return saved node
395         return Inode_CacheNode(Disk->CacheID, &retNode);
396 }

UCC git Repository :: git.ucc.asn.au