Kernel - MMap fixed (non-fixed mappings, bad search), SHM mmap only working
[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                 Log_Notice("VFS", "Nodetype '%s' doesn't have a Truncate method", h->Node->Type->TypeName);
267                 errno = ENOTIMPL;
268                 return -1;      
269         }
270         
271         return h->Node->Type->Truncate(h->Node, Size);
272 }
273
274 /**
275  * \fn int VFS_IOCtl(int FD, int ID, void *Buffer)
276  * \brief Call an IO Control on a file
277  */
278 int VFS_IOCtl(int FD, int ID, void *Buffer)
279 {
280         tVFS_Handle     *h = VFS_GetHandle(FD);
281         if(!h) {
282                 LOG("FD%i is invalid", FD);
283                 errno = EINVAL;
284                 return -1;
285         }
286
287         if(!h->Node->Type || !h->Node->Type->IOCtl) {
288                 LOG("FD%i does not have an IOCtl method");
289                 errno = EINVAL;
290                 return -1;
291         }
292         return h->Node->Type->IOCtl(h->Node, ID, Buffer);
293 }
294
295 /**
296  * \fn int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs)
297  * \brief Retrieve file information
298  * \return Number of ACLs stored
299  */
300 int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs)
301 {
302         tVFS_Handle     *h;
303          int    max;
304         
305         h = VFS_GetHandle(FD);
306         if(!h)  return -1;
307
308         if( h->Mount )
309                 Dest->mount = h->Mount->Identifier;
310         else
311                 Dest->mount = 0;
312         Dest->inode = h->Node->Inode;   
313         Dest->uid = h->Node->UID;
314         Dest->gid = h->Node->GID;
315         Dest->size = h->Node->Size;
316         Dest->atime = h->Node->ATime;
317         Dest->ctime = h->Node->MTime;
318         Dest->mtime = h->Node->CTime;
319         Dest->numacls = h->Node->NumACLs;
320         
321         Dest->flags = 0;
322         if(h->Node->Flags & VFS_FFLAG_DIRECTORY)        Dest->flags |= 0x10;
323         if(h->Node->Flags & VFS_FFLAG_SYMLINK)  Dest->flags |= 0x20;
324         
325         max = (MaxACLs < h->Node->NumACLs) ? MaxACLs : h->Node->NumACLs;
326         memcpy(&Dest->acls, h->Node->ACLs, max*sizeof(tVFS_ACL));
327         
328         return max;
329 }
330
331 // === EXPORTS ===
332 EXPORT(VFS_Read);
333 EXPORT(VFS_Write);
334 EXPORT(VFS_ReadAt);
335 EXPORT(VFS_WriteAt);
336 EXPORT(VFS_IOCtl);
337 EXPORT(VFS_Seek);
338 EXPORT(VFS_Tell);

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