Support read/write device mode
[captive.git] / src / libcaptive / client / file.c
1 /* $Id$
2  * captive vfs 'file' interface to reactos
3  * Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "captive/client-file.h"        /* self */
23 #include "lib.h"
24 #include <glib/gmessages.h>
25 #include "captive/unicode.h"
26 #include "reactos/ntos/types.h" /* for HANDLE */
27 #include "reactos/ddk/iotypes.h"        /* for IO_STATUS_BLOCK */
28 #include "reactos/ddk/iofuncs.h"        /* for IoCreateFile() */
29 #include "captive/sandbox.h"
30
31
32 static gpointer captive_file_object_parent_class=NULL;
33
34
35 static GnomeVFSResult captive_file_close(CaptiveFileObject *captive_file_object);
36
37 static void captive_file_object_finalize(CaptiveFileObject *captive_file_object)
38 {
39 GnomeVFSResult errvfsresult;
40
41         g_return_if_fail(captive_file_object!=NULL);
42
43         errvfsresult=captive_file_close(captive_file_object);
44         g_assert(errvfsresult==GNOME_VFS_OK);
45
46         G_OBJECT_CLASS(captive_file_object_parent_class)->finalize((GObject *)captive_file_object);
47 }
48
49
50 static void captive_file_object_class_init(CaptiveFileObjectClass *class)
51 {
52 GObjectClass *gobject_class=G_OBJECT_CLASS(class);
53
54         captive_file_object_parent_class=g_type_class_ref(G_TYPE_OBJECT);
55         gobject_class->finalize=(void (*)(GObject *object))captive_file_object_finalize;
56 }
57
58
59 static void captive_file_object_init(CaptiveFileObject *captive_file_object)
60 {
61         captive_file_object->file_Handle=NULL;
62 }
63
64
65 GType captive_file_object_get_type(void)
66 {
67 static GType captive_file_object_type=0;
68
69         if (!captive_file_object_type) {
70 static const GTypeInfo captive_file_object_info={
71                                 sizeof(CaptiveFileObjectClass),
72                                 NULL,   /* base_init */
73                                 NULL,   /* base_finalize */
74                                 (GClassInitFunc)captive_file_object_class_init,
75                                 NULL,   /* class_finalize */
76                                 NULL,   /* class_data */
77                                 sizeof(CaptiveFileObject),
78                                 5,      /* n_preallocs */
79                                 (GInstanceInitFunc)captive_file_object_init,
80                                 };
81
82                 captive_file_object_type=g_type_register_static(G_TYPE_OBJECT,
83                                 "CaptiveFileObject",&captive_file_object_info,0);
84                 }
85
86         return captive_file_object_type;
87 }
88
89
90 static GnomeVFSResult captive_file_new_internal(CaptiveFileObject **captive_file_object_return,
91                 const gchar *pathname,GnomeVFSOpenMode mode,gboolean create,gboolean create_exclusive,guint create_perm)
92 {
93 IO_STATUS_BLOCK file_IoStatusBlock;
94 GnomeVFSResult errvfsresult;
95 OBJECT_ATTRIBUTES file_ObjectAttributes;
96 HANDLE file_Handle;
97 CaptiveFileObject *captive_file_object;
98 NTSTATUS err;
99
100         if (CAPTIVE_IS_SANDBOX_PARENT())
101                 return captive_sandbox_parent_file_new_open(captive_file_object_return,pathname,mode);
102
103         g_return_val_if_fail(captive_file_object_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
104         g_return_val_if_fail(pathname!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
105
106         *captive_file_object_return=NULL;
107
108         errvfsresult=captive_ObjectAttributes_init(pathname,&file_ObjectAttributes);
109         g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
110         
111         /* open */
112         err=IoCreateFile(
113                         &file_Handle,   /* FileHandle */
114                         0
115                                         |(!(mode&GNOME_VFS_OPEN_READ ) ? 0 : FILE_READ_DATA)
116                                         |(!(mode&GNOME_VFS_OPEN_WRITE) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA)
117                                         ,       /* DesiredAccess */
118                         &file_ObjectAttributes, /* ObjectAttributes */
119                         &file_IoStatusBlock,    /* IoStatusBlock */
120                         NULL,   /* AllocationSize; ignored for open */
121                         (!create || create_perm&0200 ? FILE_ATTRIBUTE_NORMAL: FILE_ATTRIBUTE_READONLY), /* FileAttributes; ignored for open */
122                         (!create || !create_exclusive ? FILE_SHARE_DELETE : 0), /* ShareAccess; 0 means exclusive */
123                         (!create ? FILE_OPEN : FILE_CREATE),    /* CreateDisposition */
124                         /* FILE_SYNCHRONOUS_IO_{,NON}ALERT: We need to allow W32 filesystem
125                                  * any waits to not to let it return STATUS_CANT_WAIT us.
126                                  * Alertability should have only effect on asynchronous events
127                                  * from KeWaitForSingleObject() by setting/clearing its parameter 'Alertable'.
128                                  */
129                         FILE_SYNCHRONOUS_IO_ALERT,      /* CreateOptions */
130                         NULL,   /* EaBuffer */
131                         0,      /* EaLength */
132                         CreateFileTypeNone,     /* CreateFileType */
133                         NULL,   /* ExtraCreateParameters */
134                         0);     /* Options */
135         g_free(file_ObjectAttributes.ObjectName);       /* left from captive_file_uri_parent_init() */
136         g_return_val_if_fail(NT_SUCCESS(err)==NT_SUCCESS(file_IoStatusBlock.Status),GNOME_VFS_ERROR_GENERIC);
137         if (err==STATUS_OBJECT_NAME_COLLISION)
138                 return GNOME_VFS_ERROR_FILE_EXISTS;
139         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
140         g_return_val_if_fail(file_IoStatusBlock.Information
141                                         ==(!create ? FILE_OPENED : FILE_CREATED),
142                         GNOME_VFS_ERROR_GENERIC);
143
144         captive_file_object=g_object_new(
145                         CAPTIVE_FILE_TYPE_OBJECT,       /* object_type */
146                         NULL);  /* first_property_name; FIXME: support properties */
147
148         captive_file_object->file_Handle=file_Handle;
149         captive_file_object->offset=0;
150
151         *captive_file_object_return=captive_file_object;
152         return GNOME_VFS_OK;
153 }
154
155
156 GnomeVFSResult captive_file_new_open
157                 (CaptiveFileObject **captive_file_object_return,const gchar *pathname,GnomeVFSOpenMode mode)
158 {
159         g_return_val_if_fail(captive_file_object_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
160         g_return_val_if_fail(pathname!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
161
162         return captive_file_new_internal(captive_file_object_return,pathname,mode,
163                         FALSE,  /* create */
164                         FALSE,  /* create_exclusive; ignored */
165                         0);     /* create_perm; ignored */
166 }
167
168
169 GnomeVFSResult captive_file_new_create
170                 (CaptiveFileObject **captive_file_object_return,const gchar *pathname,GnomeVFSOpenMode mode,gboolean exclusive,guint perm)
171 {
172         g_return_val_if_fail(captive_file_object_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
173         g_return_val_if_fail(pathname!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
174
175         return captive_file_new_internal(captive_file_object_return,pathname,mode,
176                         TRUE,   /* create */
177                         exclusive,      /* create_exclusive */
178                         perm);  /* create_perm */
179 }
180
181
182 static GnomeVFSResult captive_file_close(CaptiveFileObject *captive_file_object)
183 {
184 NTSTATUS err;
185 HANDLE file_Handle;
186
187         if (CAPTIVE_IS_SANDBOX_PARENT())
188                 return captive_sandbox_parent_file_close(captive_file_object);
189
190         g_return_val_if_fail(captive_file_object!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
191         g_return_val_if_fail(captive_file_object->file_Handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);    /* already closed */
192
193         file_Handle=captive_file_object->file_Handle;
194         captive_file_object->file_Handle=NULL;
195         err=NtClose(file_Handle);
196         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
197
198         return GNOME_VFS_OK;
199 }
200
201
202 GnomeVFSResult captive_file_read(CaptiveFileObject *captive_file_object,
203                 gpointer buffer,GnomeVFSFileSize num_bytes,GnomeVFSFileSize *bytes_read_return)
204 {
205 NTSTATUS err;
206 IO_STATUS_BLOCK file_IoStatusBlock;
207 LARGE_INTEGER file_offset;
208
209         if (CAPTIVE_IS_SANDBOX_PARENT())
210                 return captive_sandbox_parent_file_read(captive_file_object,buffer,num_bytes,bytes_read_return);
211
212         g_return_val_if_fail(captive_file_object!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
213         g_return_val_if_fail(captive_file_object->file_Handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
214         g_return_val_if_fail(buffer!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
215         g_return_val_if_fail(bytes_read_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
216         g_return_val_if_fail(num_bytes==(ULONG)num_bytes,GNOME_VFS_ERROR_BAD_PARAMETERS);
217
218         file_offset.QuadPart=captive_file_object->offset;
219         err=NtReadFile(
220                         captive_file_object->file_Handle,       /* FileHandle */
221                         NULL,   /* EventHandle; completion signalling; optional */
222                         NULL,   /* ApcRoutine; optional */
223                         NULL,   /* ApcContext; optional */
224                         &file_IoStatusBlock,    /* IoStatusBlock */
225                         buffer, /* Buffer */
226                         num_bytes,      /* Length */
227                         &file_offset,   /* ByteOffset */
228                         NULL);  /* Key; NULL means no file locking key */
229         if (err==STATUS_END_OF_FILE) {
230                 g_return_val_if_fail(file_IoStatusBlock.Status==STATUS_END_OF_FILE,GNOME_VFS_ERROR_GENERIC);
231                 g_return_val_if_fail(file_IoStatusBlock.Information==0,GNOME_VFS_ERROR_GENERIC);
232                 *bytes_read_return=0;
233                 return GNOME_VFS_ERROR_EOF;
234                 }
235         g_return_val_if_fail(NT_SUCCESS(err)==NT_SUCCESS(file_IoStatusBlock.Status),GNOME_VFS_ERROR_GENERIC);
236         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
237         g_return_val_if_fail(file_IoStatusBlock.Information<=num_bytes,GNOME_VFS_ERROR_GENERIC);
238
239         captive_file_object->offset+=file_IoStatusBlock.Information;
240         *bytes_read_return=file_IoStatusBlock.Information;
241         return GNOME_VFS_OK;
242 }
243
244
245 GnomeVFSResult captive_file_write(CaptiveFileObject *captive_file_object,
246                 gconstpointer buffer,GnomeVFSFileSize num_bytes,GnomeVFSFileSize *bytes_written_return)
247 {
248 NTSTATUS err;
249 IO_STATUS_BLOCK file_IoStatusBlock;
250 LARGE_INTEGER file_offset;
251
252         if (CAPTIVE_IS_SANDBOX_PARENT())
253                 return captive_sandbox_parent_file_write(captive_file_object,buffer,num_bytes,bytes_written_return);
254
255         g_return_val_if_fail(captive_file_object!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
256         g_return_val_if_fail(captive_file_object->file_Handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
257         g_return_val_if_fail(buffer!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
258         g_return_val_if_fail(bytes_written_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
259         g_return_val_if_fail(num_bytes==(ULONG)num_bytes,GNOME_VFS_ERROR_BAD_PARAMETERS);
260
261         file_offset.QuadPart=captive_file_object->offset;
262         err=NtWriteFile(
263                         captive_file_object->file_Handle,       /* FileHandle */
264                         NULL,   /* Event; completion signalling; optional */
265                         NULL,   /* ApcRoutine; optional */
266                         NULL,   /* ApcContext; optional */
267                         &file_IoStatusBlock,    /* IoStatusBlock */
268                         (gpointer /* de-const */ )buffer,       /* Buffer */
269                         num_bytes,      /* Length */
270                         &file_offset,   /* ByteOffset */
271                         NULL);  /* Key; NULL means no file locking key */
272         g_return_val_if_fail(NT_SUCCESS(err)==NT_SUCCESS(file_IoStatusBlock.Status),GNOME_VFS_ERROR_GENERIC);
273         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
274         g_return_val_if_fail(file_IoStatusBlock.Information==num_bytes,GNOME_VFS_ERROR_GENERIC);
275
276         captive_file_object->offset+=file_IoStatusBlock.Information;
277         *bytes_written_return=file_IoStatusBlock.Information;
278         return GNOME_VFS_OK;
279 }
280
281
282 GnomeVFSResult captive_file_seek
283                 (CaptiveFileObject *captive_file_object,GnomeVFSSeekPosition whence,GnomeVFSFileOffset offset)
284 {
285         if (CAPTIVE_IS_SANDBOX_PARENT())
286                 return captive_sandbox_parent_file_seek(captive_file_object,whence,offset);
287
288         g_return_val_if_fail(captive_file_object!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
289         g_return_val_if_fail(captive_file_object->file_Handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
290
291         switch (whence) {
292                 case GNOME_VFS_SEEK_START:
293                         captive_file_object->offset=offset;
294                         break;
295                 case GNOME_VFS_SEEK_CURRENT:
296                         if (0
297                                         || (offset>0 && (captive_file_object->offset+offset)<captive_file_object->offset)
298                                         || (offset<0 && (captive_file_object->offset+offset)>captive_file_object->offset))
299                                 return GNOME_VFS_ERROR_BAD_PARAMETERS;
300                         captive_file_object->offset+=offset;
301                         break;
302                 case GNOME_VFS_SEEK_END:
303                         g_assert_not_reached(); /* NOT IMPLEMENTED YET */
304                         break;
305                 default:
306                         g_return_val_if_reached(GNOME_VFS_ERROR_BAD_PARAMETERS);
307                 }
308
309         return GNOME_VFS_OK;
310 }
311
312
313 GnomeVFSResult captive_file_tell(CaptiveFileObject *captive_file_object,GnomeVFSFileOffset *offset_return)
314 {
315         if (CAPTIVE_IS_SANDBOX_PARENT())
316                 return captive_sandbox_parent_file_tell(captive_file_object,offset_return);
317
318         g_return_val_if_fail(captive_file_object!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
319         g_return_val_if_fail(captive_file_object->file_Handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
320         g_return_val_if_fail(offset_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
321
322         *offset_return=captive_file_object->offset;
323         return GNOME_VFS_OK;
324 }