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

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