Kernel/VFS - Truncate support, mmap fixes
[tpg/acess2.git] / KernelLand / Kernel / vfs / io.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * vfs/io.c
6  * - VFS IO Handling (Read/Write)
7  *
8  * TODO: VFS-level caching to support non-blocking IO?
9  */
10 #define DEBUG   0
11 #include <acess.h>
12 #include <vfs.h>
13 #include <vfs_ext.h>
14 #include <vfs_int.h>
15
16 // === CODE ===
17 /**
18  * \fn Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer)
19  * \brief Read data from a node (file)
20  */
21 size_t VFS_Read(int FD, size_t Length, void *Buffer)
22 {
23         tVFS_Handle     *h;
24         size_t  ret;
25         
26         h = VFS_GetHandle(FD);
27         if(!h) {
28                 LOG("FD%i is a bad Handle", FD);
29                 return -1;
30         }
31         
32         if( !(h->Mode & VFS_OPENFLAG_READ) ) {
33                 LOG("FD%i not open for reading", FD);
34                 return -1;
35         }
36         if( (h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
37                 LOG("FD%i is a directory", FD);
38                 return -1;
39         }
40
41         if(!h->Node->Type || !h->Node->Type->Read) {
42                 LOG("FD%i has no read method", FD);
43                 return -1;
44         }
45
46         if( !MM_GetPhysAddr(h->Node->Type->Read) ) {
47                 Log_Error("VFS", "Node type %p(%s) read method is junk %p",
48                         h->Node->Type, h->Node, h->Node->Type->TypeName,
49                         h->Node->Type->Read);
50                 return -1;
51         }
52         
53         Uint    flags = 0;
54         flags |= (h->Mode & VFS_OPENFLAG_NONBLOCK) ? VFS_IOFLAG_NOBLOCK : 0;
55         ret = h->Node->Type->Read(h->Node, h->Position, Length, Buffer, flags);
56         if(ret != Length)       LOG("%i/%i read", ret, Length);
57         if(ret == (size_t)-1)   return -1;
58         
59         h->Position += ret;
60         return ret;
61 }
62
63 /**
64  * \fn Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer)
65  * \brief Read data from a given offset (atomic)
66  */
67 size_t VFS_ReadAt(int FD, Uint64 Offset, size_t Length, void *Buffer)
68 {
69         tVFS_Handle     *h;
70         size_t  ret;
71         
72         h = VFS_GetHandle(FD);
73         if(!h)  return -1;
74         
75         if( !(h->Mode & VFS_OPENFLAG_READ) )    return -1;
76         if( h->Node->Flags & VFS_FFLAG_DIRECTORY )      return -1;
77
78         if( !h->Node->Type || !h->Node->Type->Read) {
79                 Warning("VFS_ReadAt - Node %p, does not have a read method", h->Node);
80                 return 0;
81         }
82
83         if( !MM_GetPhysAddr(h->Node->Type->Read) ) {
84                 Log_Error("VFS", "Node type %p(%s) read method is junk %p",
85                         h->Node->Type, h->Node->Type->TypeName,
86                         h->Node->Type->Read);
87         }
88         
89         Uint    flags = 0;
90         flags |= (h->Mode & VFS_OPENFLAG_NONBLOCK) ? VFS_IOFLAG_NOBLOCK : 0;
91         ret = h->Node->Type->Read(h->Node, Offset, Length, Buffer, flags);
92         if(ret != Length)       LOG("%i/%i read", ret, Length);
93         if(ret == (size_t)-1)   return -1;
94         return ret;
95 }
96
97 /**
98  * \fn Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer)
99  * \brief Read data from a node (file)
100  */
101 size_t VFS_Write(int FD, size_t Length, const void *Buffer)
102 {
103         tVFS_Handle     *h = VFS_GetHandle(FD);
104         if(!h) {
105                 LOG("FD%i is not open", FD);
106                 errno = EBADF;
107                 return -1;
108         }
109         
110         size_t ret = VFS_WriteAt(FD, h->Position, Length, Buffer);
111         if(ret == (size_t)-1)   return -1;
112
113         if( !(h->Node->Type->Flags & VFS_NODETYPEFLAG_STREAM) )
114                 h->Position += ret;
115         return ret;
116 }
117
118 /**
119  * \fn Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer)
120  * \brief Write data to a file at a given offset
121  */
122 size_t VFS_WriteAt(int FD, Uint64 Offset, size_t Length, const void *Buffer)
123 {
124         LOG("FD=%i,Offset=%lli,Length=%i,Buffer=%p",
125                 FD, Offset, Length, Buffer);
126         tVFS_Handle     *h = VFS_GetHandle(FD);
127         if(!h)  return -1;
128         
129         if( !(h->Mode & VFS_OPENFLAG_WRITE) ) {
130                 LOG("FD%i not opened for writing", FD);
131                 errno = EBADF;
132                 return -1;
133         }
134         if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) {
135                 LOG("FD%i is a directory", FD);
136                 errno = EISDIR;
137                 return -1;
138         }
139
140         const tVFS_NodeType*    nodetype = h->Node->Type;
141         if(!nodetype || !nodetype->Write) {
142                 LOG("FD%i has no write method", FD);
143                 errno = EINTERNAL;
144                 return 0;
145         }
146         
147         if( !MM_GetPhysAddr(nodetype->Write) ) {
148                 Log_Error("VFS", "Node type %p(%s) write method is junk %p",
149                         nodetype, nodetype->TypeName, nodetype->Write);
150                 errno = EINTERNAL;
151                 return -1;
152         }
153         
154         // Bounds checks
155         if( h->Node->Size != (Uint64)-1 && Offset > h->Node->Size ) {
156                 Log_Notice("VFS", "Write starting past EOF of FD%x (%lli > %lli)",
157                         FD, Offset, h->Node->Size);
158                 //errno = ESPIPE;
159                 return 0;
160         }
161         if( Offset + Length > h->Node->Size )
162         {
163                 // Going OOB
164                 if( !nodetype->Truncate )
165                 {
166                         LOG("No .Truncate method, emiting write past EOF");
167                 }
168                 else if( nodetype->Flags & VFS_NODETYPEFLAG_NOAUTOEXPAND )
169                 {
170                         LOG("NOAUTOEXPAND set, truncating length from %i to %i",
171                                 Length, h->Node->Size - Offset);
172                         Length = h->Node->Size - Offset;
173                 }
174                 else if( nodetype->Truncate(h->Node, Offset + Length) != Offset + Length )
175                 {
176                         // oh... fail? Truncate to current size
177                         LOG(".Truncate failed, truncating length from %i to %i",
178                                 Length, h->Node->Size - Offset);
179                         Length = h->Node->Size - Offset;
180                 }
181                 else
182                 {
183                         // Expansion, node size should now fit
184                         LOG("Expanded file");
185                 }
186         }
187         
188         // Create flag set
189         Uint flags = 0;
190         flags |= (h->Mode & VFS_OPENFLAG_NONBLOCK) ? VFS_IOFLAG_NOBLOCK : 0;
191         
192         // Dispatch the read!
193         size_t ret = nodetype->Write(h->Node, Offset, Length, Buffer, flags);
194         if(ret == (size_t)-1)   return -1;
195         if(ret != Length)       LOG("%i/%i written", ret, Length);
196
197         return ret;
198 }
199
200 /**
201  * \fn Uint64 VFS_Tell(int FD)
202  * \brief Returns the current file position
203  */
204 Uint64 VFS_Tell(int FD)
205 {
206         tVFS_Handle     *h;
207         
208         h = VFS_GetHandle(FD);
209         if(!h)  return -1;
210         
211         return h->Position;
212 }
213
214 /**
215  * \fn int VFS_Seek(int FD, Sint64 Offset, int Whence)
216  * \brief Seek to a new location
217  * \param FD    File descriptor
218  * \param Offset        Where to go
219  * \param Whence        From where
220  */
221 int VFS_Seek(int FD, Sint64 Offset, int Whence)
222 {
223         tVFS_Handle     *h = VFS_GetHandle(FD);
224         if(!h)  return -1;
225         
226         if( (h->Node->Type->Flags & VFS_NODETYPEFLAG_STREAM) ) {
227                 LOG("Seeking in stream");
228                 return -1;
229         }
230         
231         // Set relative to current position
232         if(Whence == 0) {
233                 LOG("(FD%x)->Position += %lli", FD, Offset);
234                 h->Position += Offset;
235                 return 0;
236         }
237         
238         // Set relative to end of file
239         if(Whence < 0) {
240                 if( h->Node->Size == (Uint64)-1 )       return -1;
241
242                 LOG("(FD%x)->Position = %llx - %llx", FD, h->Node->Size, Offset);
243                 h->Position = h->Node->Size - Offset;
244                 return 0;
245         }
246         
247         // Set relative to start of file
248         LOG("(FD%x)->Position = %llx", FD, Offset);
249         h->Position = Offset;
250         return 0;
251 }
252
253 /*
254  * Truncate/Expand a file's allocation
255  */
256 off_t VFS_Truncate(int FD, off_t Size)
257 {
258         tVFS_Handle     *h = VFS_GetHandle(FD);
259         if(!h) {
260                 errno = EBADF;
261                 return -1;
262         }
263         
264         if( !h->Node->Type->Truncate)
265         {
266                 errno = ENOTIMPL;
267                 return -1;      
268         }
269         
270         return h->Node->Type->Truncate(h->Node, Size);
271 }
272
273 /**
274  * \fn int VFS_IOCtl(int FD, int ID, void *Buffer)
275  * \brief Call an IO Control on a file
276  */
277 int VFS_IOCtl(int FD, int ID, void *Buffer)
278 {
279         tVFS_Handle     *h = VFS_GetHandle(FD);
280         if(!h) {
281                 LOG("FD%i is invalid", FD);
282                 errno = EINVAL;
283                 return -1;
284         }
285
286         if(!h->Node->Type || !h->Node->Type->IOCtl) {
287                 LOG("FD%i does not have an IOCtl method");
288                 errno = EINVAL;
289                 return -1;
290         }
291         return h->Node->Type->IOCtl(h->Node, ID, Buffer);
292 }
293
294 /**
295  * \fn int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs)
296  * \brief Retrieve file information
297  * \return Number of ACLs stored
298  */
299 int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs)
300 {
301         tVFS_Handle     *h;
302          int    max;
303         
304         h = VFS_GetHandle(FD);
305         if(!h)  return -1;
306
307         if( h->Mount )
308                 Dest->mount = h->Mount->Identifier;
309         else
310                 Dest->mount = 0;
311         Dest->inode = h->Node->Inode;   
312         Dest->uid = h->Node->UID;
313         Dest->gid = h->Node->GID;
314         Dest->size = h->Node->Size;
315         Dest->atime = h->Node->ATime;
316         Dest->ctime = h->Node->MTime;
317         Dest->mtime = h->Node->CTime;
318         Dest->numacls = h->Node->NumACLs;
319         
320         Dest->flags = 0;
321         if(h->Node->Flags & VFS_FFLAG_DIRECTORY)        Dest->flags |= 0x10;
322         if(h->Node->Flags & VFS_FFLAG_SYMLINK)  Dest->flags |= 0x20;
323         
324         max = (MaxACLs < h->Node->NumACLs) ? MaxACLs : h->Node->NumACLs;
325         memcpy(&Dest->acls, h->Node->ACLs, max*sizeof(tVFS_ACL));
326         
327         return max;
328 }
329
330 // === EXPORTS ===
331 EXPORT(VFS_Read);
332 EXPORT(VFS_Write);
333 EXPORT(VFS_ReadAt);
334 EXPORT(VFS_WriteAt);
335 EXPORT(VFS_IOCtl);
336 EXPORT(VFS_Seek);
337 EXPORT(VFS_Tell);

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