4039a7c0256ea8c6d24a0107899b6a673f7cd635
[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_Unlink(tVFS_Node *Node, const char *OldName);
20  int    Ext2_Link(tVFS_Node *Parent, const char *Name, tVFS_Node *Node);
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         .Unlink = Ext2_Unlink,
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, .i_size=0x%x}", inode.i_block[0], inode.i_size);
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 && size <= inode.i_size)
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                         if( Base == 0 ) {
87                                 size = 0;
88                                 break;
89                         }
90                 }
91         }
92         
93         // Check for the end of the list
94         if(size <= 0 || size > inode.i_size) {
95                 LEAVE('n');
96                 return NULL;
97         }
98         
99         // Read Entry
100         VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
101         LOG("dirent={.rec_len=%i,.inode=0x%x,.name_len=%i}",
102                 dirent.rec_len, dirent.inode, dirent.name_len);
103         dirent.name[ dirent.name_len ] = '\0';  // Cap off string
104         
105         if( dirent.name_len == 0 ) {
106                 LEAVE('p', VFS_SKIP);
107                 return VFS_SKIP;
108         }
109         
110         // Ignore . and .. (these are done in the VFS)
111         if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
112         ||  (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
113                 LEAVE('p', VFS_SKIP);
114                 return VFS_SKIP;        // Skip
115         }
116         
117         LEAVE('s', dirent.name);
118         // Create new node
119         return strdup(dirent.name);
120 }
121
122 /**
123  * \brief Gets information about a file
124  * \param Node  Parent Node
125  * \param Filename      Name of wanted file
126  * \return VFS Node of file
127  */
128 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
129 {
130         tExt2_Disk      *disk = Node->ImplPtr;
131         tExt2_Inode     inode;
132         tExt2_DirEnt    dirent;
133         Uint64  Base;   // Block's Base Address
134          int    block = 0;
135         Uint    ofs = 0;
136          int    entNum = 0;
137         Uint    size;
138          int    filenameLen = strlen(Filename);
139         
140         // Read directory's inode
141         Ext2_int_ReadInode(disk, Node->Inode, &inode);
142         size = inode.i_size;
143         
144         // Get First Block
145         // - Do this ourselves as it is a simple operation
146         Base = inode.i_block[0] * disk->BlockSize;
147         // Find File
148         while(size > 0)
149         {
150                 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
151                 // TODO: Possible overrun if name_len == 255?
152                 dirent.name[ dirent.name_len ] = '\0';  // Cap off string
153                 // If it matches, create a node and return it
154                 if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
155                         return Ext2_int_CreateNode( disk, dirent.inode );
156                 // Increment pointers
157                 ofs += dirent.rec_len;
158                 size -= dirent.rec_len;
159                 entNum ++;
160                 
161                 // Check for end of block
162                 if(ofs >= disk->BlockSize) {
163                         block ++;
164                         if( ofs > disk->BlockSize ) {
165                                 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
166                                         entNum-1, Node->Inode);
167                         }
168                         ofs = 0;
169                         Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
170                 }
171         }
172         
173         return NULL;
174 }
175
176 /**
177  * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
178  * \brief Create a new node
179  */
180 int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
181 {
182         ENTER("pParent sName xFlags", Parent, Name, Flags);
183         
184         Uint64 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
185         if( inodeNum == 0 ) {
186                 return -1;
187         }
188         tVFS_Node *child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
189         if( !child ) {
190                 Ext2_int_DereferenceInode(Parent->ImplPtr, inodeNum);
191                 return -1;
192         }
193
194         child->Flags = Flags & (VFS_FFLAG_DIRECTORY|VFS_FFLAG_SYMLINK|VFS_FFLAG_READONLY);
195         child->UID = Threads_GetUID();
196         child->GID = Threads_GetGID();
197         child->CTime =
198                 child->MTime =
199                 child->ATime =
200                 now();
201         child->ImplInt = 0;     // ImplInt is the link count
202         // TODO: Set up ACLs
203
204         int rv = Ext2_Link(Parent, Name, child);
205         child->Type->Close(child);
206         LEAVE('i', rv);
207         return rv;
208 }
209
210 /**
211  * \brief Rename a file
212  * \param Node  This (directory) node
213  * \param OldName       Old name of file
214  * \param NewName       New name for file
215  * \return Boolean Failure - See ::tVFS_Node.Unlink for info
216  */
217 int Ext2_Unlink(tVFS_Node *Node, const char *OldName)
218 {
219         return 1;
220 }
221
222 /**
223  * \brief Links an existing node to a new name
224  * \param Parent        Parent (directory) node
225  * \param Name  New name for the node
226  * \param Node  Node to link
227  * \return Boolean Failure - See ::tVFS_Node.Link for info
228  */
229 int Ext2_Link(tVFS_Node *Node, const char *Name, tVFS_Node *Child)
230 {       
231         #if 0
232         tExt2_Disk      *disk = Node->ImplPtr;
233         tExt2_Inode     inode;
234         tExt2_DirEnt    dirent;
235         tExt2_DirEnt    newEntry;
236         Uint64  Base;   // Block's Base Address
237          int    block = 0, ofs = 0;
238         Uint    size;
239         void    *blockData;
240          int    bestMatch = -1, bestSize, bestBlock, bestOfs;
241          int    nEntries;
242         
243         blockData = malloc(disk->BlockSize);
244         
245         // Read child inode (get's the file type)
246         Ext2_int_ReadInode(disk, Child->Inode, &inode);
247         
248         // Create a stub entry
249         newEntry.inode = Child->Inode;
250         newEntry.name_len = strlen(Name);
251         newEntry.rec_len = (newEntry.name_len+3+8)&~3;
252         newEntry.type = inode.i_mode >> 12;
253         memcpy(newEntry.name, Name, newEntry.name_len);
254         
255         // Read directory's inode
256         Ext2_int_ReadInode(disk, Node->Inode, &inode);
257         size = inode.i_size;
258         
259         // Get a lock on the inode
260         Ext2_int_LockInode(disk, Node->Inode);
261         
262         // Get First Block
263         // - Do this ourselves as it is a simple operation
264         base = inode.i_block[0] * disk->BlockSize;
265         VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
266         block = 0;
267         // Find File
268         while(size > 0)
269         {
270                 dirent = blockData + ofs;
271                 // Sanity Check the entry
272                 if(ofs + dirent->rec_len > disk->BlockSize) {
273                         Log_Warning("EXT2",
274                                 "Directory entry %i of inode 0x%x extends over a block boundary",
275                                 nEntries, (Uint)Node->Inode);
276                 }
277                 else {
278                 
279                         // Free entry
280                         if(dirent->type == 0) {
281                                 if( dirent->rec_len >= newEntry.rec_len
282                                  && (bestMatch == -1 || bestSize > dirent->rec_len) )
283                                 {
284                                         bestMatch = nEntries;
285                                         bestSize = dirent->rec_len;
286                                         bestBlock = block;
287                                         bestOfs = ofs;
288                                 }
289                         }
290                         // Non free - check name to avoid duplicates
291                         else {
292                                 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
293                                         Ext2_int_UnlockInode(disk, Node->Inode);
294                                         return 1;       // ERR_???
295                                 }
296                         }
297                 }
298                 
299                 // Increment the pointer
300                 nEntries ++;
301                 ofs += dirent->rec_len;
302                 if( ofs >= disk->BlockSize ) {
303                         // Read the next block if needed
304                         BLOCK_DIR_OFS(Node->Data, block) = nEntries;
305                         block ++;
306                         ofs = 0;
307                         base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
308                         VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
309                 }
310         }
311         
312         // Check if a free slot was found
313         if( bestMatch >= 0 ) {
314                 // Read-Modify-Write
315                 bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
316                 if( block > 0 )
317                         bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
318                 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
319                 dirent = blockData + bestOfs;
320                 memcpy(dirent, newEntry, newEntry.rec_len);
321                 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
322         }
323         else {
324                 // Allocate block, Write
325                 block = Ext2_int_AllocateBlock(Disk, block);
326                 Log_Warning("EXT2", "");
327         }
328
329         Ext2_int_UnlockInode(disk, Node->Inode);
330         return 0;
331         #else
332         return 1;
333         #endif
334 }
335
336 // ---- INTERNAL FUNCTIONS ----
337 /**
338  * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
339  * \brief Create a new VFS Node
340  */
341 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
342 {
343         tExt2_Inode     inode;
344         tVFS_Node       retNode;
345         tVFS_Node       *tmpNode;
346         
347         if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
348                 return NULL;
349         
350         if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
351                 return tmpNode;
352
353         memset(&retNode, 0, sizeof(retNode));   
354         
355         // Set identifiers
356         retNode.Inode = InodeID;
357         retNode.ImplPtr = Disk;
358         
359         // Set file length
360         retNode.Size = inode.i_size;
361         
362         // Set Access Permissions
363         retNode.UID = inode.i_uid;
364         retNode.GID = inode.i_gid;
365         retNode.NumACLs = 3;
366         retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
367         
368         //  Set Function Pointers
369         retNode.Type = &gExt2_FileType;
370         
371         switch(inode.i_mode & EXT2_S_IFMT)
372         {
373         // Symbolic Link
374         case EXT2_S_IFLNK:
375                 retNode.Flags = VFS_FFLAG_SYMLINK;
376                 break;
377         // Regular File
378         case EXT2_S_IFREG:
379                 retNode.Flags = 0;
380                 retNode.Size |= (Uint64)inode.i_dir_acl << 32;
381                 break;
382         // Directory
383         case EXT2_S_IFDIR:
384                 retNode.Type = &gExt2_DirType;
385                 retNode.Flags = VFS_FFLAG_DIRECTORY;
386                 retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
387                 break;
388         // Unknown, Write protect it to be safe 
389         default:
390                 retNode.Flags = VFS_FFLAG_READONLY;
391                 break;
392         }
393         
394         // Set Timestamps
395         retNode.ATime = inode.i_atime * 1000;
396         retNode.MTime = inode.i_mtime * 1000;
397         retNode.CTime = inode.i_ctime * 1000;
398         
399         // Save in node cache and return saved node
400         return Inode_CacheNode(Disk->CacheID, &retNode);
401 }

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