ed26d6b00acf614a7e42a0f1415d92734bf8c930
[captive.git] / src / client / libcaptive-gnomevfs / gnome-vfs-method.c
1 /* $Id$
2  * gnome-vfs init/shutdown implementation of interface to libcaptive
3  * Copyright (C) 2002 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 "giognomevfs.h"
23 #include <libgnomevfs/gnome-vfs-method.h>
24 #include <glib/gmessages.h>
25 #include <string.h>
26 #include "reactos/ntos/types.h" /* for HANDLE */
27 #include "reactos/ddk/iotypes.h"        /* for IO_STATUS_BLOCK */
28 #include "captive/macros.h"
29 #include "reactos/ddk/rtl.h"    /* for InitializeObjectAttributes() */
30 #include "captive/unicode.h"
31 #include "reactos/ddk/iofuncs.h"        /* for IoCreateFile() */
32 #include "gnome-vfs-module.h"
33 #include <libgnomevfs/gnome-vfs-utils.h>        /* for gnome_vfs_unescape_string() */
34
35
36 static GnomeVFSMethod GnomeVFSMethod_static;
37 G_LOCK_DEFINE_STATIC(GnomeVFSMethod_static);
38
39
40 /* FIXME: multi-parent */
41 G_LOCK_DEFINE_STATIC(captive_gnomevfs_uri_parent);      /* covers also 'captive_gnomevfs_uri_parent_giognomevfs' */
42 static gchar *captive_gnomevfs_uri_parent;
43 static gchar *captive_gnomevfs_uri_parent_fs_path;
44 static struct captive_gnomevfs_giognomevfs *captive_gnomevfs_uri_parent_giognomevfs;
45
46
47 /* function will leave g_malloc()ed 'ObjectAttributes->ObjectName' if ObjectAttributes !
48  */
49 static GnomeVFSResult captive_gnomevfs_uri_parent_init(GnomeVFSURI *uri,OBJECT_ATTRIBUTES *ObjectAttributes)
50 {
51 gchar *uri_parent_string;
52 GnomeVFSResult errvfsresult;
53 gchar *w32_path,*s,*d,*vfs_path;
54
55         g_return_val_if_fail(uri!=NULL,GNOME_VFS_ERROR_INVALID_URI);
56
57         if (!uri->parent)
58                 return GNOME_VFS_ERROR_INVALID_URI;
59         if (!uri->text) /* not needed here but we don't permit non-specific fs-image reference */
60                 return GNOME_VFS_ERROR_INVALID_URI;
61         uri_parent_string=gnome_vfs_uri_to_string(uri->parent,GNOME_VFS_URI_HIDE_NONE);
62         g_assert(uri_parent_string!=NULL);
63         /* FIXME: multi-parent */
64         G_LOCK(captive_gnomevfs_uri_parent);
65         if (captive_gnomevfs_uri_parent) {
66                 g_return_val_if_fail(!strcmp(uri_parent_string,captive_gnomevfs_uri_parent),GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES);
67                 g_free(uri_parent_string);
68                 }
69         else {
70                 errvfsresult=captive_gnomevfs_giognomevfs_new(&captive_gnomevfs_uri_parent_giognomevfs,
71                                 captive_gnomevfs_uri_parent_fs_path,uri->parent);
72                 g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
73                 captive_gnomevfs_uri_parent=uri_parent_string;
74                 }
75         G_UNLOCK(captive_gnomevfs_uri_parent);
76
77         if (ObjectAttributes) {
78                 /* Do not open "\Cdfs"(anything) as it is just the filesystem implementation.
79                  * ntoskrnl/io/fs.c/IoMountVolume() will map
80                  *  FILE_DEVICE_CD_ROM -> FILE_DEVICE_CD_ROM_FILE_SYSTEM
81                  * for us automatically when opening the device itself.
82                  * If you put some trailing content (such as "\\.") there
83                  *  IoCreateFile()->ObCreateObject()->ObFindObject()
84                  * will not leave 'ObCreateObject::RemainingPath' as NULL
85                  * and later IopCreateFile() would not consider it FO_DIRECT_DEVICE_OPEN (e.g. w/o any direct mount!).
86                  * On the other side it will somehow get managed automatically and it works even
87                  * without the trailing "\\." for root directory open - don't ask me why. :-)
88                  */
89                 vfs_path=gnome_vfs_unescape_string(     /* decode %xy escaping */
90                                 uri->text,      /* escaped */
91                                 NULL);  /* illegal_characters */
92                 w32_path=(gchar *)/* de-const it as we can modify it but we must not free() it */
93                                 captive_printf_alloca("\\Device\\CdRom0\\%s",vfs_path);
94                 g_free(vfs_path);
95                 /* translate '/' -> '\' */
96                 for (s=w32_path;(s=strchr(s,'/'));s++)
97                         *s='\\';
98                 /* collapse multiple sequences of '\' as it is a no-no for W32 */
99                 for (s=d=w32_path;*s;s++)
100                         if (d==w32_path || !(*s=='\\' && d[-1]=='\\'))
101                                 *d++=*s;
102                 *d=0;
103                 G_LOCK(libcaptive);
104                 InitializeObjectAttributes(
105                                 ObjectAttributes,       /* InitializedAttributes */
106                                 captive_utf8_to_UnicodeString_malloc(w32_path), /* ObjectName */
107                                 0,      /* Attributes; I hope no OBJ_KERNEL_HANDLE as we are 'system process' */
108                                 NULL,   /* RootDirectory */
109                                 NULL);  /* SecurityDescriptor; ignored */
110                 G_UNLOCK(libcaptive);
111                 }
112
113         return GNOME_VFS_OK;
114 }
115
116
117 struct captive_gnomevfs_directory_handle {
118         HANDLE dir_Handle;
119         gboolean read_first;
120         /* 'QueryDirectory_buf' for NtQueryDirectoryFile() must be persistent
121          * to keep the state if !read_first
122          */
123         char QueryDirectory_buf[sizeof(FILE_ID_BOTH_DIR_INFORMATION)
124                         +0x1000 /* max 'FileName' length, 255 should be enough */ * sizeof(WCHAR /* *FILE_ID_BOTH_DIR_INFORMATION.FileName */ )];
125         };
126
127 static GnomeVFSResult captive_gnomevfs_open_directory(GnomeVFSMethod *method,
128                 GnomeVFSMethodHandle **method_handle,GnomeVFSURI *uri,GnomeVFSFileInfoOptions options,GnomeVFSContext *context)
129 {
130 GnomeVFSResult errvfsresult;
131 OBJECT_ATTRIBUTES dir_ObjectAttributes;
132 HANDLE dir_Handle;
133 IO_STATUS_BLOCK dir_IoStatusBlock;
134 NTSTATUS err;
135 struct captive_gnomevfs_directory_handle *directory_handle;
136
137         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
138         g_return_val_if_fail(method_handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
139         g_return_val_if_fail(uri!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
140
141         errvfsresult=captive_gnomevfs_uri_parent_init(uri,&dir_ObjectAttributes);
142         g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
143         
144         /* wanted: * IoCreateFile()->ObCreateObject(,,,IoFileObjectType)->
145          * ->(IoFileObjectType->Create==IopCreateFile)()->IoMountVolume()
146          */
147         G_LOCK(libcaptive);
148         err=IoCreateFile(
149                         &dir_Handle,    /* FileHandle */
150                         FILE_LIST_DIRECTORY,    /* DesiredAccess */
151                         &dir_ObjectAttributes,  /* ObjectAttributes */
152                         &dir_IoStatusBlock,     /* IoStatusBlock */
153                         NULL,   /* AllocationSize; ignored for open */
154                         FILE_ATTRIBUTE_NORMAL,  /* FileAttributes; ignored for open */
155                         FILE_SHARE_WRITE,       /* ShareAccess; 0 means exclusive */
156                         FILE_OPEN,      /* CreateDisposition */
157                         /* FILE_SYNCHRONOUS_IO_{,NON}ALERT: We need to allow W32 filesystem
158                                  * any waits to not to let it return STATUS_CANT_WAIT us.
159                                  * Alertability should have only effect on asynchronous events
160                                  * from KeWaitForSingleObject() by setting/clearing its parameter 'Alertable'.
161                                  */
162                         FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_ALERT,  /* CreateOptions */
163                         NULL,   /* EaBuffer */
164                         0,      /* EaLength */
165                         CreateFileTypeNone,     /* CreateFileType */
166                         NULL,   /* ExtraCreateParameters */
167                         0);     /* Options */
168         G_UNLOCK(libcaptive);
169         g_free(dir_ObjectAttributes.ObjectName);        /* left from captive_gnomevfs_uri_parent_init() */
170         g_return_val_if_fail(NT_SUCCESS(err)==NT_SUCCESS(dir_IoStatusBlock.Status),GNOME_VFS_ERROR_GENERIC);
171         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
172         g_return_val_if_fail(dir_IoStatusBlock.Information==FILE_OPENED,GNOME_VFS_ERROR_GENERIC);
173
174         captive_new(directory_handle);
175         directory_handle->dir_Handle=dir_Handle;
176         directory_handle->read_first=TRUE;
177
178         *method_handle=(GnomeVFSMethodHandle *)directory_handle;
179         return GNOME_VFS_OK;
180 }
181
182
183 static GnomeVFSResult captive_gnomevfs_close_directory(GnomeVFSMethod *method,
184                 GnomeVFSMethodHandle *method_handle,GnomeVFSContext *context)
185 {
186 struct captive_gnomevfs_directory_handle *directory_handle;
187 NTSTATUS err;
188
189         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
190         g_return_val_if_fail(method_handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
191
192         directory_handle=(struct captive_gnomevfs_directory_handle *)method_handle;
193         G_LOCK(libcaptive);
194         err=NtClose(directory_handle->dir_Handle);
195         G_UNLOCK(libcaptive);
196         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
197
198         g_free(directory_handle);
199         return GNOME_VFS_OK;
200 }
201
202
203 static GnomeVFSResult FileIdBothDirInformation_to_GnomeVFSFileInfo(GnomeVFSFileInfo *file_info,
204                 FILE_ID_BOTH_DIR_INFORMATION *FileIdBothDirInformation,IO_STATUS_BLOCK *IoStatusBlock)
205 {
206 UNICODE_STRING FileName_UnicodeString;
207 BOOLEAN errBOOLEAN;
208 ULONG tmp_ULONG;
209
210         g_return_val_if_fail(file_info!=NULL,GNOME_VFS_ERROR_GENERIC);
211         g_return_val_if_fail(FileIdBothDirInformation!=NULL,GNOME_VFS_ERROR_GENERIC);
212
213         g_return_val_if_fail(NT_SUCCESS(IoStatusBlock->Status),GNOME_VFS_ERROR_GENERIC);
214         /* do not exceed the returned buffer by this record;
215          * '->NextEntryOffset' is ==0 as we used 'ReturnSingleEntry==TRUE'
216          */
217         g_assert((gpointer)(((char *)FileIdBothDirInformation)+FileIdBothDirInformation->NextEntryOffset)
218                         <=(gpointer)(((char *)FileIdBothDirInformation)+IoStatusBlock->Information));
219
220         file_info->valid_fields=0;
221
222         FileName_UnicodeString.Length=FileIdBothDirInformation->FileNameLength;
223         FileName_UnicodeString.MaximumLength=FileIdBothDirInformation->FileNameLength
224                         +sizeof(*FileIdBothDirInformation->FileName);   /* 0-terminator */
225         g_assert((gpointer)(((char *)FileIdBothDirInformation->FileName)+FileName_UnicodeString.Length)
226                         <=(gpointer)(((char *)FileIdBothDirInformation)+IoStatusBlock->Information));
227                                         /* ensure we fit below '->IoStatusBlock->Information' at least without the 0-terminator */
228         FileIdBothDirInformation->FileName[FileIdBothDirInformation->FileNameLength
229                         /sizeof(*FileIdBothDirInformation->FileName)]=0;        /* 0-terminate it */
230         FileName_UnicodeString.Buffer=FileIdBothDirInformation->FileName;
231         file_info->name=captive_UnicodeString_to_utf8_malloc(&FileName_UnicodeString);
232         /* '->name' assumed for 'file_info->valid_fields' */
233
234         /* FIXME: What is 'FILE_ATTRIBUTE_NORMAL'? */
235         switch (FileIdBothDirInformation->FileAttributes & (0
236                         | FILE_ATTRIBUTE_DIRECTORY
237                         | FILE_ATTRIBUTE_DEVICE)) {
238                 case 0:                        file_info->type=GNOME_VFS_FILE_TYPE_REGULAR;   break;
239                 case FILE_ATTRIBUTE_DIRECTORY: file_info->type=GNOME_VFS_FILE_TYPE_DIRECTORY; break;
240                 case FILE_ATTRIBUTE_DEVICE:    file_info->type=GNOME_VFS_FILE_TYPE_SOCKET;
241                         /* or GNOME_VFS_FILE_TYPE_CHARACTER_DEVICE or GNOME_VFS_FILE_TYPE_BLOCK_DEVICE ? */
242                         break;
243                 default:                       file_info->type=GNOME_VFS_FILE_TYPE_UNKNOWN;   break;
244                 }
245         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_TYPE;
246
247         /* we use 0600 for r/w files, 0400 for FILE_ATTRIBUTE_READONLY */
248         file_info->permissions=GNOME_VFS_PERM_USER_READ;
249         if (file_info->type==GNOME_VFS_FILE_TYPE_DIRECTORY)
250                 file_info->permissions|=GNOME_VFS_PERM_USER_EXEC;
251         if (!(FileIdBothDirInformation->FileAttributes & FILE_ATTRIBUTE_READONLY))
252                 file_info->permissions=GNOME_VFS_PERM_USER_WRITE;
253         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
254
255         file_info->size=FileIdBothDirInformation->EndOfFile.QuadPart;
256         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_SIZE;
257
258         file_info->block_count=FileIdBothDirInformation->AllocationSize.QuadPart/512;
259         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_BLOCK_COUNT;
260
261         file_info->flags=GNOME_VFS_FILE_FLAGS_LOCAL;
262         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_FLAGS;
263
264         if (FileIdBothDirInformation->LastAccessTime.QuadPart) {        /* it may be 0 if not set */
265                 errBOOLEAN=RtlTimeToSecondsSince1970(&FileIdBothDirInformation->LastAccessTime,&tmp_ULONG);
266                 g_assert(errBOOLEAN==TRUE);
267                 file_info->atime=tmp_ULONG;
268                 file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_ATIME;
269                 }
270
271         /* it may be 0 if not set */
272         if (FileIdBothDirInformation->LastWriteTime.QuadPart || FileIdBothDirInformation->ChangeTime.QuadPart) {
273                 errBOOLEAN=RtlTimeToSecondsSince1970(
274                                 /* take the more recent (==bigger) time: */
275                                 (FileIdBothDirInformation->LastWriteTime.QuadPart > FileIdBothDirInformation->ChangeTime.QuadPart
276                                                 ? &FileIdBothDirInformation->LastWriteTime : &FileIdBothDirInformation->ChangeTime),
277                                 &tmp_ULONG);
278                 g_assert(errBOOLEAN==TRUE);
279                 file_info->mtime=tmp_ULONG;
280                 file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_MTIME;
281                 }
282
283         if (FileIdBothDirInformation->CreationTime.QuadPart) {  /* it may be 0 if not set */
284                 errBOOLEAN=RtlTimeToSecondsSince1970(&FileIdBothDirInformation->CreationTime,&tmp_ULONG);
285                 g_assert(errBOOLEAN==TRUE);
286                 file_info->ctime=tmp_ULONG;
287                 file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_CTIME;
288                 }
289
290         return GNOME_VFS_OK;
291 }
292
293
294 static GnomeVFSResult captive_gnomevfs_read_directory(GnomeVFSMethod *method,
295                 GnomeVFSMethodHandle *method_handle,GnomeVFSFileInfo *file_info,GnomeVFSContext *context)
296 {
297 struct captive_gnomevfs_directory_handle *directory_handle;
298 NTSTATUS err;
299 IO_STATUS_BLOCK dir_IoStatusBlock;
300 FILE_ID_BOTH_DIR_INFORMATION *FileIdBothDirInformation;
301 GnomeVFSResult errvfsresult;
302
303         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
304         g_return_val_if_fail(method_handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
305         g_return_val_if_fail(file_info!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
306
307         directory_handle=(struct captive_gnomevfs_directory_handle *)method_handle;
308
309         FileIdBothDirInformation=(void *)directory_handle->QueryDirectory_buf;
310         G_LOCK(libcaptive);
311         err=NtQueryDirectoryFile(
312                         directory_handle->dir_Handle,   /* FileHandle */
313                         NULL,   /* PEvent; completion signalling; optional */
314                         NULL,   /* ApcRoutine; optional */
315                         NULL,   /* ApcContext; optional */
316                         &dir_IoStatusBlock,     /* IoStatusBlock */
317                         (gpointer)directory_handle->QueryDirectory_buf, /* FileInformation */
318                         sizeof(directory_handle->QueryDirectory_buf)    /* Length */
319                                         -sizeof(*FileIdBothDirInformation->FileName),   /* reserve space for 0-terminator */
320                         FileIdBothDirectoryInformation, /* FileInformationClass; =>FILE_ID_BOTH_DIR_INFORMATION */
321                         TRUE,   /* ReturnSingleEntry */
322                         NULL,   /* FileName; wildcards possible; optional */
323                         directory_handle->read_first);  /* RestartScan */
324         G_UNLOCK(libcaptive);
325         directory_handle->read_first=FALSE;
326         if (err==STATUS_NO_MORE_FILES)
327                 return GNOME_VFS_ERROR_EOF;
328         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
329
330         errvfsresult=FileIdBothDirInformation_to_GnomeVFSFileInfo(file_info,FileIdBothDirInformation,
331                         &dir_IoStatusBlock);
332         g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
333
334         return GNOME_VFS_OK;
335 }
336
337
338 struct captive_gnomevfs_file_handle {
339         HANDLE file_Handle;
340         GnomeVFSFileOffset offset;
341         };
342
343 static GnomeVFSResult captive_gnomevfs_open(GnomeVFSMethod *method,
344                 GnomeVFSMethodHandle **method_handle_return,GnomeVFSURI *uri,GnomeVFSOpenMode mode,GnomeVFSContext *context)
345 {
346 IO_STATUS_BLOCK file_IoStatusBlock;
347 GnomeVFSResult errvfsresult;
348 OBJECT_ATTRIBUTES file_ObjectAttributes;
349 HANDLE file_Handle;
350 struct captive_gnomevfs_file_handle *file_handle;
351 NTSTATUS err;
352
353         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
354         g_return_val_if_fail(method_handle_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
355
356         /* init */
357         errvfsresult=captive_gnomevfs_uri_parent_init(uri,&file_ObjectAttributes);
358         g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
359         
360         /* open */
361         G_LOCK(libcaptive);
362         err=IoCreateFile(
363                         &file_Handle,   /* FileHandle */
364                         0
365                                         |(!(mode&GNOME_VFS_OPEN_READ ) ? 0 : FILE_READ_DATA)
366                                         |(!(mode&GNOME_VFS_OPEN_WRITE) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA)
367                                         ,       /* DesiredAccess */
368                         &file_ObjectAttributes, /* ObjectAttributes */
369                         &file_IoStatusBlock,    /* IoStatusBlock */
370                         NULL,   /* AllocationSize; ignored for open */
371                         FILE_ATTRIBUTE_NORMAL,  /* FileAttributes; ignored for open */
372                         FILE_SHARE_WRITE,       /* ShareAccess; 0 means exclusive */
373                         FILE_OPEN,      /* CreateDisposition */
374                         /* FILE_SYNCHRONOUS_IO_{,NON}ALERT: We need to allow W32 filesystem
375                                  * any waits to not to let it return STATUS_CANT_WAIT us.
376                                  * Alertability should have only effect on asynchronous events
377                                  * from KeWaitForSingleObject() by setting/clearing its parameter 'Alertable'.
378                                  */
379                         FILE_SYNCHRONOUS_IO_ALERT,      /* CreateOptions */
380                         NULL,   /* EaBuffer */
381                         0,      /* EaLength */
382                         CreateFileTypeNone,     /* CreateFileType */
383                         NULL,   /* ExtraCreateParameters */
384                         0);     /* Options */
385         G_UNLOCK(libcaptive);
386         g_free(file_ObjectAttributes.ObjectName);       /* left from captive_gnomevfs_uri_parent_init() */
387         g_return_val_if_fail(NT_SUCCESS(err)==NT_SUCCESS(file_IoStatusBlock.Status),GNOME_VFS_ERROR_GENERIC);
388         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
389         g_return_val_if_fail(file_IoStatusBlock.Information==FILE_OPENED,GNOME_VFS_ERROR_GENERIC);
390
391         captive_new(file_handle);
392         file_handle->file_Handle=file_Handle;
393         file_handle->offset=0;
394
395         *method_handle_return=(GnomeVFSMethodHandle *)file_handle;
396         return GNOME_VFS_OK;
397 }
398
399
400 static GnomeVFSResult captive_gnomevfs_close(GnomeVFSMethod *method,
401                 GnomeVFSMethodHandle *method_handle,GnomeVFSContext *context)
402 {
403 struct captive_gnomevfs_file_handle *file_handle;
404 NTSTATUS err;
405
406         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
407         g_return_val_if_fail(method_handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
408
409         file_handle=(struct captive_gnomevfs_file_handle *)method_handle;
410         G_LOCK(libcaptive);
411         err=NtClose(file_handle->file_Handle);
412         G_UNLOCK(libcaptive);
413         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
414
415         g_free(file_handle);
416         return GNOME_VFS_OK;
417 }
418
419
420 static GnomeVFSResult captive_gnomevfs_read(GnomeVFSMethod *method,GnomeVFSMethodHandle *method_handle,
421                 gpointer buffer,GnomeVFSFileSize num_bytes,GnomeVFSFileSize *bytes_read_return,GnomeVFSContext *context)
422 {
423 struct captive_gnomevfs_file_handle *file_handle;
424 NTSTATUS err;
425 IO_STATUS_BLOCK file_IoStatusBlock;
426 LARGE_INTEGER file_offset;
427
428         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
429         g_return_val_if_fail(method_handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
430         g_return_val_if_fail(buffer!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
431         g_return_val_if_fail(bytes_read_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
432         g_return_val_if_fail(num_bytes==(ULONG)num_bytes,GNOME_VFS_ERROR_BAD_PARAMETERS);
433
434         file_handle=(struct captive_gnomevfs_file_handle *)method_handle;
435         file_offset.QuadPart=file_handle->offset;
436         G_LOCK(libcaptive);
437         err=NtReadFile(
438                         file_handle->file_Handle,       /* FileHandle */
439                         NULL,   /* EventHandle; completion signalling; optional */
440                         NULL,   /* ApcRoutine; optional */
441                         NULL,   /* ApcContext; optional */
442                         &file_IoStatusBlock,    /* IoStatusBlock */
443                         buffer, /* Buffer */
444                         num_bytes,      /* Length */
445                         &file_offset,   /* ByteOffset */
446                         NULL);  /* Key; NULL means no file locking key */
447         G_UNLOCK(libcaptive);
448         if (err==STATUS_END_OF_FILE) {
449                 g_return_val_if_fail(file_IoStatusBlock.Status==STATUS_END_OF_FILE,GNOME_VFS_ERROR_GENERIC);
450                 g_return_val_if_fail(file_IoStatusBlock.Information==0,GNOME_VFS_ERROR_GENERIC);
451                 *bytes_read_return=0;
452                 return GNOME_VFS_ERROR_EOF;
453                 }
454         g_return_val_if_fail(NT_SUCCESS(err)==NT_SUCCESS(file_IoStatusBlock.Status),GNOME_VFS_ERROR_GENERIC);
455         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
456         g_return_val_if_fail(file_IoStatusBlock.Information<=num_bytes,GNOME_VFS_ERROR_GENERIC);
457
458         file_handle->offset+=file_IoStatusBlock.Information;
459         *bytes_read_return=file_IoStatusBlock.Information;
460         return GNOME_VFS_OK;
461 }
462
463
464 static GnomeVFSResult captive_gnomevfs_seek(GnomeVFSMethod *method,
465                 GnomeVFSMethodHandle *method_handle,GnomeVFSSeekPosition whence,GnomeVFSFileOffset offset,GnomeVFSContext *context)
466 {
467 struct captive_gnomevfs_file_handle *file_handle;
468
469         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
470         g_return_val_if_fail(method_handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
471
472         file_handle=(struct captive_gnomevfs_file_handle *)method_handle;
473         switch (whence) {
474                 case GNOME_VFS_SEEK_START:
475                         file_handle->offset=offset;
476                         break;
477                 case GNOME_VFS_SEEK_CURRENT:
478                         if (0
479                                         || (offset>0 && (file_handle->offset+offset)<file_handle->offset)
480                                         || (offset<0 && (file_handle->offset+offset)>file_handle->offset))
481                                 return GNOME_VFS_ERROR_BAD_PARAMETERS;
482                         file_handle->offset+=offset;
483                         break;
484                 case GNOME_VFS_SEEK_END:
485                         g_assert_not_reached(); /* NOT IMPLEMENTED YET */
486                         break;
487                 default:
488                         g_return_val_if_reached(GNOME_VFS_ERROR_BAD_PARAMETERS);
489                 }
490
491         return GNOME_VFS_OK;
492 }
493
494 static GnomeVFSResult captive_gnomevfs_tell(GnomeVFSMethod *method,
495                 GnomeVFSMethodHandle *method_handle,GnomeVFSFileOffset *offset_return)
496 {
497 struct captive_gnomevfs_file_handle *file_handle;
498
499         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
500         g_return_val_if_fail(method_handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
501         g_return_val_if_fail(offset_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
502
503         file_handle=(struct captive_gnomevfs_file_handle *)method_handle;
504         *offset_return=file_handle->offset;
505         return GNOME_VFS_OK;
506 }
507
508
509 static GnomeVFSResult captive_gnomevfs_create(GnomeVFSMethod *method,
510                 GnomeVFSMethodHandle **method_handle_return,GnomeVFSURI *uri,GnomeVFSOpenMode mode,gboolean exclusive,guint perm,
511                 GnomeVFSContext *context)
512 {
513         g_return_val_if_reached(GNOME_VFS_ERROR_NOT_SUPPORTED); /* FIXME: NOT IMPLEMENTED YET */
514 }
515
516
517 static gboolean captive_gnomevfs_is_local(GnomeVFSMethod *method,const GnomeVFSURI *uri)
518 {
519         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
520         g_return_val_if_fail(uri!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
521
522         return gnome_vfs_uri_is_local(uri->parent);
523 }
524
525
526 /* Use 'FileAllInformationStruct' identifier instead of the logical 'FileAllInformation'
527  * to prevent override of enum member 'FileAllInformation'
528  */
529 static GnomeVFSResult FileAllInformationStruct_to_GnomeVFSFileInfo(GnomeVFSFileInfo *file_info,
530                 FILE_ALL_INFORMATION *FileAllInformationStruct,IO_STATUS_BLOCK *IoStatusBlock)
531 {
532 UNICODE_STRING FileName_UnicodeString;
533 BOOLEAN errBOOLEAN;
534 ULONG tmp_ULONG;
535
536         g_return_val_if_fail(file_info!=NULL,GNOME_VFS_ERROR_GENERIC);
537         g_return_val_if_fail(FileAllInformationStruct!=NULL,GNOME_VFS_ERROR_GENERIC);
538
539         g_return_val_if_fail(NT_SUCCESS(IoStatusBlock->Status),GNOME_VFS_ERROR_GENERIC);
540
541         file_info->valid_fields=0;
542
543         FileName_UnicodeString.Length=FileAllInformationStruct->NameInformation.FileNameLength;
544         FileName_UnicodeString.MaximumLength=FileAllInformationStruct->NameInformation.FileNameLength
545                         +sizeof(*FileAllInformationStruct->NameInformation.FileName);   /* 0-terminator */
546         g_assert((gpointer)(((char *)FileAllInformationStruct->NameInformation.FileName)+FileName_UnicodeString.Length)
547                         <=(gpointer)(((char *)FileAllInformationStruct)+IoStatusBlock->Information));
548                                         /* ensure we fit below '->IoStatusBlock->Information' at least without the 0-terminator */
549         FileAllInformationStruct->NameInformation.FileName[FileAllInformationStruct->NameInformation.FileNameLength
550                         /sizeof(*FileAllInformationStruct->NameInformation.FileName)]=0;        /* 0-terminate it */
551         FileName_UnicodeString.Buffer=FileAllInformationStruct->NameInformation.FileName;
552         file_info->name=captive_UnicodeString_to_utf8_malloc(&FileName_UnicodeString);
553         /* '->name' assumed for 'file_info->valid_fields' */
554
555         /* FIXME: What is 'FILE_ATTRIBUTE_NORMAL'? */
556         switch (FileAllInformationStruct->BasicInformation.FileAttributes & (0
557                         | FILE_ATTRIBUTE_DIRECTORY
558                         | FILE_ATTRIBUTE_DEVICE)) {
559                 case 0:                        file_info->type=GNOME_VFS_FILE_TYPE_REGULAR;   break;
560                 case FILE_ATTRIBUTE_DIRECTORY: file_info->type=GNOME_VFS_FILE_TYPE_DIRECTORY; break;
561                 case FILE_ATTRIBUTE_DEVICE:    file_info->type=GNOME_VFS_FILE_TYPE_SOCKET;
562                         /* or GNOME_VFS_FILE_TYPE_CHARACTER_DEVICE or GNOME_VFS_FILE_TYPE_BLOCK_DEVICE ? */
563                         break;
564                 default:                       file_info->type=GNOME_VFS_FILE_TYPE_UNKNOWN;   break;
565                 }
566         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_TYPE;
567
568         /* we use 0600 for r/w files, 0400 for FILE_ATTRIBUTE_READONLY */
569         file_info->permissions=GNOME_VFS_PERM_USER_READ;
570         if (file_info->type==GNOME_VFS_FILE_TYPE_DIRECTORY)
571                 file_info->permissions|=GNOME_VFS_PERM_USER_EXEC;
572         if (!(FileAllInformationStruct->BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY))
573                 file_info->permissions=GNOME_VFS_PERM_USER_WRITE;
574         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
575
576         file_info->size=FileAllInformationStruct->StandardInformation.EndOfFile.QuadPart;
577         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_SIZE;
578
579         file_info->block_count=FileAllInformationStruct->StandardInformation.AllocationSize.QuadPart/512;
580         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_BLOCK_COUNT;
581
582         file_info->flags=GNOME_VFS_FILE_FLAGS_LOCAL;
583         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_FLAGS;
584
585         if (FileAllInformationStruct->BasicInformation.LastAccessTime.QuadPart) {       /* it may be 0 if not set */
586                 errBOOLEAN=RtlTimeToSecondsSince1970(&FileAllInformationStruct->BasicInformation.LastAccessTime,&tmp_ULONG);
587                 g_assert(errBOOLEAN==TRUE);
588                 file_info->atime=tmp_ULONG;
589                 file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_ATIME;
590                 }
591
592         /* it may be 0 if not set */
593         if (FileAllInformationStruct->BasicInformation.LastWriteTime.QuadPart || FileAllInformationStruct->BasicInformation.ChangeTime.QuadPart) {
594                 errBOOLEAN=RtlTimeToSecondsSince1970(
595                                 /* take the more recent (==bigger) time: */
596                                 (FileAllInformationStruct->BasicInformation.LastWriteTime.QuadPart > FileAllInformationStruct->BasicInformation.ChangeTime.QuadPart
597                                                 ? &FileAllInformationStruct->BasicInformation.LastWriteTime : &FileAllInformationStruct->BasicInformation.ChangeTime),
598                                 &tmp_ULONG);
599                 g_assert(errBOOLEAN==TRUE);
600                 file_info->mtime=tmp_ULONG;
601                 file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_MTIME;
602                 }
603
604         if (FileAllInformationStruct->BasicInformation.CreationTime.QuadPart) { /* it may be 0 if not set */
605                 errBOOLEAN=RtlTimeToSecondsSince1970(&FileAllInformationStruct->BasicInformation.CreationTime,&tmp_ULONG);
606                 g_assert(errBOOLEAN==TRUE);
607                 file_info->ctime=tmp_ULONG;
608                 file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_CTIME;
609                 }
610
611         return GNOME_VFS_OK;
612 }
613
614
615 static GnomeVFSResult captive_gnomevfs_get_file_info(GnomeVFSMethod *method,
616                 GnomeVFSURI *uri,GnomeVFSFileInfo *file_info,GnomeVFSFileInfoOptions options,GnomeVFSContext *context)
617 {
618 NTSTATUS err;
619 IO_STATUS_BLOCK file_IoStatusBlock;
620 FILE_ALL_INFORMATION *FileAllInformationStruct;
621 GnomeVFSResult errvfsresult;
622 OBJECT_ATTRIBUTES file_ObjectAttributes;
623 HANDLE file_Handle;
624 char QueryFile_buf[sizeof(FILE_ALL_INFORMATION)
625                 +0x1000 /* max 'FileName' length, 255 should be enough */ * sizeof(WCHAR /* *FILE_ALL_INFORMATION.NameInformation.FileName */ )];
626
627         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
628         g_return_val_if_fail(file_info!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
629         /* handle 'options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE'? */
630
631         /* init */
632         errvfsresult=captive_gnomevfs_uri_parent_init(uri,&file_ObjectAttributes);
633         g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
634         
635         /* open */
636         G_LOCK(libcaptive);
637         err=IoCreateFile(
638                         &file_Handle,   /* FileHandle */
639                         FILE_READ_ATTRIBUTES,   /* DesiredAccess */
640                         &file_ObjectAttributes, /* ObjectAttributes */
641                         &file_IoStatusBlock,    /* IoStatusBlock */
642                         NULL,   /* AllocationSize; ignored for open */
643                         FILE_ATTRIBUTE_NORMAL,  /* FileAttributes; ignored for open */
644                         FILE_SHARE_WRITE,       /* ShareAccess; 0 means exclusive */
645                         FILE_OPEN,      /* CreateDisposition */
646                         /* FILE_SYNCHRONOUS_IO_{,NON}ALERT: We need to allow W32 filesystem
647                                  * any waits to not to let it return STATUS_CANT_WAIT us.
648                                  * Alertability should have only effect on asynchronous events
649                                  * from KeWaitForSingleObject() by setting/clearing its parameter 'Alertable'.
650                                  */
651                         FILE_SYNCHRONOUS_IO_ALERT,      /* CreateOptions */
652                         NULL,   /* EaBuffer */
653                         0,      /* EaLength */
654                         CreateFileTypeNone,     /* CreateFileType */
655                         NULL,   /* ExtraCreateParameters */
656                         0);     /* Options */
657         G_UNLOCK(libcaptive);
658         g_free(file_ObjectAttributes.ObjectName);       /* left from captive_gnomevfs_uri_parent_init() */
659         g_return_val_if_fail(NT_SUCCESS(err)==NT_SUCCESS(file_IoStatusBlock.Status),GNOME_VFS_ERROR_GENERIC);
660         if (err==STATUS_OBJECT_NAME_NOT_FOUND)
661                 return GNOME_VFS_ERROR_NOT_FOUND;
662         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
663         g_return_val_if_fail(file_IoStatusBlock.Information==FILE_OPENED,GNOME_VFS_ERROR_GENERIC);
664
665         /* query */
666         FileAllInformationStruct=(void *)QueryFile_buf;
667         G_LOCK(libcaptive);
668         err=NtQueryInformationFile(
669                         file_Handle,    /* FileHandle */
670                         &file_IoStatusBlock,    /* IoStatusBlock */
671                         (gpointer)QueryFile_buf,        /* FileInformation */
672                         sizeof(QueryFile_buf)   /* Length */
673                                         -sizeof(*FileAllInformationStruct->NameInformation.FileName),   /* reserve space for 0-terminator */
674                         FileAllInformation);    /* FileInformationClass; =>FILE_ALL_INFORMATION */
675         G_UNLOCK(libcaptive);
676         if (!NT_SUCCESS(err)) {
677                 g_assert_not_reached();
678                 goto err_close;
679                 }
680
681         /* close */
682         G_LOCK(libcaptive);
683         err=NtClose(file_Handle);
684         G_UNLOCK(libcaptive);
685         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
686
687         /* process gathered info */
688         errvfsresult=FileAllInformationStruct_to_GnomeVFSFileInfo(file_info,FileAllInformationStruct,
689                         &file_IoStatusBlock);
690         g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
691
692         return GNOME_VFS_OK;
693
694 err_close:
695         G_LOCK(libcaptive);
696         err=NtClose(file_Handle);
697         G_UNLOCK(libcaptive);
698         g_return_val_if_fail(NT_SUCCESS(err),GNOME_VFS_ERROR_GENERIC);
699 /* err: */
700         g_return_val_if_reached(GNOME_VFS_ERROR_GENERIC);
701 }
702
703
704 /**
705  * captive_gnomevfs_init:
706  *
707  * Returns: Initialized structure of #GnomeVFSMethod with static methods of libcaptive-gnomevfs.
708  */
709 GnomeVFSMethod *captive_gnomevfs_method_init(const gchar *fs_path)
710 {
711         G_LOCK(captive_gnomevfs_uri_parent);
712         captive_gnomevfs_uri_parent_fs_path=g_strdup(fs_path);
713         G_UNLOCK(captive_gnomevfs_uri_parent);
714
715         G_LOCK(GnomeVFSMethod_static);
716         CAPTIVE_MEMZERO(&GnomeVFSMethod_static);
717         GnomeVFSMethod_static.method_table_size=sizeof(GnomeVFSMethod_static);
718         GnomeVFSMethod_static.open_directory =captive_gnomevfs_open_directory;
719         GnomeVFSMethod_static.close_directory=captive_gnomevfs_close_directory;
720         GnomeVFSMethod_static.read_directory =captive_gnomevfs_read_directory;
721         GnomeVFSMethod_static.open           =captive_gnomevfs_open;    /* mandatory */
722         GnomeVFSMethod_static.close          =captive_gnomevfs_close;
723         GnomeVFSMethod_static.read           =captive_gnomevfs_read;
724         GnomeVFSMethod_static.seek           =captive_gnomevfs_seek;
725         GnomeVFSMethod_static.tell           =captive_gnomevfs_tell;
726         GnomeVFSMethod_static.create         =captive_gnomevfs_create;  /* mandatory */
727         GnomeVFSMethod_static.is_local       =captive_gnomevfs_is_local;        /* mandatory */
728         GnomeVFSMethod_static.get_file_info  =captive_gnomevfs_get_file_info;   /* mandatory */
729         G_UNLOCK(GnomeVFSMethod_static);
730
731         return &GnomeVFSMethod_static;
732 }