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

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