Modules/EXT2 - Working on write support
[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", 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         ENTER("pParent sName xFlags", Parent, Name, Flags);
176         
177         Uint64 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
178         if( inodeNum == 0 ) {
179                 return -1;
180         }
181         tVFS_Node *child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
182         if( !child ) {
183                 Ext2_int_DereferenceInode(Parent->ImplPtr, inodeNum);
184                 return -1;
185         }
186
187         child->Flags = Flags & (VFS_FFLAG_DIRECTORY|VFS_FFLAG_SYMLINK|VFS_FFLAG_READONLY);
188         child->UID = Threads_GetUID();
189         child->GID = Threads_GetGID();
190         child->CTime =
191                 child->MTime =
192                 child->ATime =
193                 now();
194         child->ImplInt = 0;     // ImplInt is the link count
195         // TODO: Set up ACLs
196
197         int rv = Ext2_Link(Parent, Name, child);
198         child->Type->Close(child);
199         LEAVE('i', rv);
200         return rv;
201 }
202
203 /**
204  * \brief Rename a file
205  * \param Node  This (directory) node
206  * \param OldName       Old name of file
207  * \param NewName       New name for file
208  * \return Boolean Failure - See ::tVFS_Node.Unlink for info
209  */
210 int Ext2_Unlink(tVFS_Node *Node, const char *OldName)
211 {
212         return 1;
213 }
214
215 /**
216  * \brief Links an existing node to a new name
217  * \param Parent        Parent (directory) node
218  * \param Name  New name for the node
219  * \param Node  Node to link
220  * \return Boolean Failure - See ::tVFS_Node.Link for info
221  */
222 int Ext2_Link(tVFS_Node *Node, const char *Name, tVFS_Node *Child)
223 {       
224         #if 0
225         tExt2_Disk      *disk = Node->ImplPtr;
226         tExt2_Inode     inode;
227         tExt2_DirEnt    dirent;
228         tExt2_DirEnt    newEntry;
229         Uint64  Base;   // Block's Base Address
230          int    block = 0, ofs = 0;
231         Uint    size;
232         void    *blockData;
233          int    bestMatch = -1, bestSize, bestBlock, bestOfs;
234          int    nEntries;
235         
236         blockData = malloc(disk->BlockSize);
237         
238         // Read child inode (get's the file type)
239         Ext2_int_ReadInode(disk, Child->Inode, &inode);
240         
241         // Create a stub entry
242         newEntry.inode = Child->Inode;
243         newEntry.name_len = strlen(Name);
244         newEntry.rec_len = (newEntry.name_len+3+8)&~3;
245         newEntry.type = inode.i_mode >> 12;
246         memcpy(newEntry.name, Name, newEntry.name_len);
247         
248         // Read directory's inode
249         Ext2_int_ReadInode(disk, Node->Inode, &inode);
250         size = inode.i_size;
251         
252         // Get a lock on the inode
253         Ext2_int_LockInode(disk, Node->Inode);
254         
255         // Get First Block
256         // - Do this ourselves as it is a simple operation
257         base = inode.i_block[0] * disk->BlockSize;
258         VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
259         block = 0;
260         // Find File
261         while(size > 0)
262         {
263                 dirent = blockData + ofs;
264                 // Sanity Check the entry
265                 if(ofs + dirent->rec_len > disk->BlockSize) {
266                         Log_Warning("EXT2",
267                                 "Directory entry %i of inode 0x%x extends over a block boundary",
268                                 nEntries, (Uint)Node->Inode);
269                 }
270                 else {
271                 
272                         // Free entry
273                         if(dirent->type == 0) {
274                                 if( dirent->rec_len >= newEntry.rec_len
275                                  && (bestMatch == -1 || bestSize > dirent->rec_len) )
276                                 {
277                                         bestMatch = nEntries;
278                                         bestSize = dirent->rec_len;
279                                         bestBlock = block;
280                                         bestOfs = ofs;
281                                 }
282                         }
283                         // Non free - check name to avoid duplicates
284                         else {
285                                 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
286                                         Ext2_int_UnlockInode(disk, Node->Inode);
287                                         return 1;       // ERR_???
288                                 }
289                         }
290                 }
291                 
292                 // Increment the pointer
293                 nEntries ++;
294                 ofs += dirent->rec_len;
295                 if( ofs >= disk->BlockSize ) {
296                         // Read the next block if needed
297                         BLOCK_DIR_OFS(Node->Data, block) = nEntries;
298                         block ++;
299                         ofs = 0;
300                         base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
301                         VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
302                 }
303         }
304         
305         // Check if a free slot was found
306         if( bestMatch >= 0 ) {
307                 // Read-Modify-Write
308                 bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
309                 if( block > 0 )
310                         bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
311                 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
312                 dirent = blockData + bestOfs;
313                 memcpy(dirent, newEntry, newEntry.rec_len);
314                 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
315         }
316         else {
317                 // Allocate block, Write
318                 block = Ext2_int_AllocateBlock(Disk, block);
319                 Log_Warning("EXT2", "");
320         }
321
322         Ext2_int_UnlockInode(disk, Node->Inode);
323         return 0;
324         #else
325         return 1;
326         #endif
327 }
328
329 // ---- INTERNAL FUNCTIONS ----
330 /**
331  * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
332  * \brief Create a new VFS Node
333  */
334 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
335 {
336         tExt2_Inode     inode;
337         tVFS_Node       retNode;
338         tVFS_Node       *tmpNode;
339         
340         if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
341                 return NULL;
342         
343         if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
344                 return tmpNode;
345         
346         
347         // Set identifiers
348         retNode.Inode = InodeID;
349         retNode.ImplPtr = Disk;
350         
351         // Set file length
352         retNode.Size = inode.i_size;
353         retNode.Data = NULL;
354         
355         // Set Access Permissions
356         retNode.UID = inode.i_uid;
357         retNode.GID = inode.i_gid;
358         retNode.NumACLs = 3;
359         retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
360         
361         //  Set Function Pointers
362         retNode.Type = &gExt2_FileType;
363         
364         switch(inode.i_mode & EXT2_S_IFMT)
365         {
366         // Symbolic Link
367         case EXT2_S_IFLNK:
368                 retNode.Flags = VFS_FFLAG_SYMLINK;
369                 break;
370         // Regular File
371         case EXT2_S_IFREG:
372                 retNode.Flags = 0;
373                 retNode.Size |= (Uint64)inode.i_dir_acl << 32;
374                 break;
375         // Directory
376         case EXT2_S_IFDIR:
377                 retNode.Type = &gExt2_DirType;
378                 retNode.Flags = VFS_FFLAG_DIRECTORY;
379                 retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
380                 break;
381         // Unknown, Write protect it to be safe 
382         default:
383                 retNode.Flags = VFS_FFLAG_READONLY;
384                 break;
385         }
386         
387         // Set Timestamps
388         retNode.ATime = inode.i_atime * 1000;
389         retNode.MTime = inode.i_mtime * 1000;
390         retNode.CTime = inode.i_ctime * 1000;
391         
392         // Save in node cache and return saved node
393         return Inode_CacheNode(Disk->CacheID, &retNode);
394 }

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