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

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