70f666835eec74d39c0dd8221c4b68f5c027da30
[ntfsprogs-gnomevfs.git] / src / gnome-vfs-method.c
1 /* $Id$
2  * gnome-vfs init/shutdown implementation of interface to libntfs
3  * Copyright (C) 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; either version 2 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20
21 #include "config.h"
22
23 #undef FALSE
24 #undef TRUE
25 #include <ntfs/types.h> /* for 'FALSE'/'TRUE' libntfs definition */
26 #define FALSE FALSE
27 #define TRUE TRUE
28
29 #include "gnome-vfs-method.h"   /* self */
30 #include <libgnomevfs/gnome-vfs-method.h>
31 #include <glib/gmessages.h>
32 #include "gnome-vfs-module.h"
33 #include <glib/ghash.h>
34 #include <string.h>
35 #include <libgnomevfs/gnome-vfs-utils.h>
36
37 #include <ntfs/volume.h>
38 #include <ntfs/dir.h>
39
40
41 static GnomeVFSMethod GnomeVFSMethod_static;
42 G_LOCK_DEFINE_STATIC(GnomeVFSMethod_static);
43
44
45 /* map: (gchar *)method_name -> (struct method_name_info *) */
46 static GHashTable *method_name_hash;
47 G_LOCK_DEFINE_STATIC(method_name_hash);
48
49 struct method_name_info {
50         gchar *args;
51         };
52
53 static void method_name_hash_key_destroy_func(gchar *key)
54 {
55         g_return_if_fail(key!=NULL);
56
57         g_free(key);
58 }
59
60 static void method_name_hash_value_destroy_func(struct method_name_info *value)
61 {
62         g_return_if_fail(value!=NULL);
63
64         g_free(value->args);
65         g_free(value);
66 }
67
68 static void method_name_hash_init(void)
69 {
70         G_LOCK(method_name_hash);
71         if (!method_name_hash) {
72                 method_name_hash=g_hash_table_new_full(
73                                 g_str_hash,     /* hash_func */
74                                 g_str_equal,    /* key_equal_func */
75                                 (GDestroyNotify)method_name_hash_key_destroy_func,      /* key_destroy_func */
76                                 (GDestroyNotify)method_name_hash_value_destroy_func);   /* value_destroy_func */
77                 }
78         G_UNLOCK(method_name_hash);
79 }
80
81
82 /* map: (gchar *)uri_parent_string "method_name:uri_parent" -> (ntfs_volume *) */
83 static GHashTable *uri_parent_string_hash;
84 G_LOCK_DEFINE_STATIC(uri_parent_string_hash);
85
86 static void uri_parent_string_hash_key_destroy_func(gchar *key)
87 {
88         g_return_if_fail(key!=NULL);
89
90         g_free(key);
91 }
92
93 static void uri_parent_string_hash_value_destroy_func(ntfs_volume *value)
94 {
95         g_return_if_fail(value!=NULL);
96
97         ntfs_umount(    /* errors ignored */
98                         value,  /* vol */
99                         TRUE);  /* force; possibly loose modifications */
100 }
101
102 static void uri_parent_string_hash_init(void)
103 {
104         G_LOCK(uri_parent_string_hash);
105         if (!uri_parent_string_hash) {
106                 uri_parent_string_hash=g_hash_table_new_full(
107                                 g_str_hash,     /* hash_func */
108                                 g_str_equal,    /* key_equal_func */
109                                 (GDestroyNotify)uri_parent_string_hash_key_destroy_func,        /* key_destroy_func */
110                                 (GDestroyNotify)uri_parent_string_hash_value_destroy_func);     /* value_destroy_func */
111                 }
112         G_UNLOCK(uri_parent_string_hash);
113 }
114
115
116 static GnomeVFSResult libntfs_gnomevfs_uri_parent_init(ntfs_volume **volume_return,GnomeVFSURI *uri)
117 {
118 gchar *uri_parent_string;
119 gchar *uri_parent_string_parent;
120 ntfs_volume *volume;
121
122         g_return_val_if_fail(uri!=NULL,GNOME_VFS_ERROR_INVALID_URI);
123         g_return_val_if_fail(volume_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
124
125         uri_parent_string_hash_init();
126
127         if (!uri->parent)
128                 return GNOME_VFS_ERROR_INVALID_URI;
129         if (!uri->text) /* not needed here but we don't permit non-specific fs-image reference */
130                 return GNOME_VFS_ERROR_INVALID_URI;
131         uri_parent_string_parent=gnome_vfs_uri_to_string(uri->parent,GNOME_VFS_URI_HIDE_NONE);
132         g_assert(uri_parent_string_parent!=NULL);
133
134         uri_parent_string=g_strdup_printf("%s:%s",uri->method_string,uri_parent_string_parent);
135         g_assert(uri_parent_string!=NULL);
136
137         G_LOCK(uri_parent_string_hash);
138         volume=g_hash_table_lookup(uri_parent_string_hash,uri_parent_string);
139         G_UNLOCK(uri_parent_string_hash);
140         if (!volume) {
141 struct method_name_info *method_name_info;
142
143                 G_LOCK(method_name_hash);
144                 method_name_info=g_hash_table_lookup(method_name_hash,uri->method_string);
145                 G_UNLOCK(method_name_hash);
146                 if (!method_name_info)
147                         g_return_val_if_reached(GNOME_VFS_ERROR_INVALID_URI);   /* should not happend */
148
149                 if (strcmp(uri->parent->method_string,"file")) {        /* TODO: Generic GnomeVFS filter. */
150                         g_free(uri_parent_string);
151                         return GNOME_VFS_ERROR_INVALID_URI;
152                         }
153
154                 if (!(volume=ntfs_mount(uri->parent->text,MS_RDONLY))) {
155                         g_free(uri_parent_string);
156                         return GNOME_VFS_ERROR_WRONG_FORMAT;
157                         }
158
159                 G_LOCK(uri_parent_string_hash);
160                 g_hash_table_insert(uri_parent_string_hash,g_strdup(uri_parent_string),volume);
161                 G_UNLOCK(uri_parent_string_hash);
162                 }
163         g_free(uri_parent_string);
164
165         *volume_return=volume;
166         return GNOME_VFS_OK;
167 }
168
169
170 static GnomeVFSResult inode_open_by_pathname(ntfs_inode **inode_return,ntfs_volume *volume,const gchar *pathname)
171 {
172 MFT_REF mref;
173 ntfs_inode *inode;
174 gchar *pathname_parse,*pathname_next;
175 int errint;
176
177         g_return_val_if_fail(inode_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
178         g_return_val_if_fail(volume!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
179         g_return_val_if_fail(pathname!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
180
181         pathname=g_path_skip_root(pathname);
182         pathname_parse=g_alloca(strlen(pathname)+1);
183         strcpy(pathname_parse,pathname);
184         mref=FILE_root;
185         for (;;) {
186 uchar_t *pathname_parse_ucs2;
187 gchar *pathname_parse_unescaped;
188 int i;
189
190                 G_LOCK(libntfs);
191                 inode=ntfs_inode_open(volume,mref);
192                 G_UNLOCK(libntfs);
193                 if (!inode)
194                         return GNOME_VFS_ERROR_NOT_FOUND;
195                 if (!*pathname_parse) {
196                         *inode_return=inode;
197                         return GNOME_VFS_OK;
198                         }
199                 for (pathname_next=pathname_parse;*pathname_next && *pathname_next!=G_DIR_SEPARATOR;pathname_next++);
200                 if (*pathname_next)
201                         *pathname_next++='\0';  /* terminate current path element */
202                 while (*pathname_next==G_DIR_SEPARATOR)
203                         pathname_next++;
204                 /* FIXME: Is 'pathname' utf8? */
205                 pathname_parse_unescaped=gnome_vfs_unescape_string(pathname_parse,
206                                 NULL);  /* illegal_characters */
207                 libntfs_newn(pathname_parse_ucs2,strlen(pathname_parse_unescaped)+1);
208                 for (i=0;pathname_parse_unescaped[i];i++)
209                         pathname_parse_ucs2[i]=pathname_parse_unescaped[i];
210                 pathname_parse_ucs2[i]=0;
211                 g_free(pathname_parse_unescaped);
212                 G_LOCK(libntfs);
213                 mref=ntfs_inode_lookup_by_name(inode,pathname_parse_ucs2,i);
214                 G_UNLOCK(libntfs);
215                 g_free(pathname_parse_ucs2);
216                 if ((MFT_REF)-1==mref)
217                         return GNOME_VFS_ERROR_NOT_FOUND;
218                 G_LOCK(libntfs);
219                 errint=ntfs_inode_close(inode);
220                 G_UNLOCK(libntfs);
221                 if (errint)
222                         g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL);
223                 pathname_parse=pathname_next;
224                 }
225         /* NOTREACHED */
226 }
227
228
229 struct libntfs_directory {
230         ntfs_inode *inode;
231         GList *file_info_list;  /* of (GnomeVFSFileInfo *); last item has ->data==NULL */
232         };
233
234 static GnomeVFSResult libntfs_gnomevfs_open_directory(GnomeVFSMethod *method,
235                 GnomeVFSMethodHandle **method_handle,GnomeVFSURI *uri,GnomeVFSFileInfoOptions options,GnomeVFSContext *context)
236 {
237 GnomeVFSResult errvfsresult;
238 ntfs_volume *volume;
239 ntfs_inode *inode;
240 struct libntfs_directory *libntfs_directory;
241
242         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
243         g_return_val_if_fail(method_handle!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
244
245         if (GNOME_VFS_OK!=(errvfsresult=libntfs_gnomevfs_uri_parent_init(&volume,uri)))
246                 return errvfsresult;
247
248         if (GNOME_VFS_OK!=(errvfsresult=inode_open_by_pathname(&inode,volume,uri->text)))
249                 return errvfsresult;
250
251         libntfs_new(libntfs_directory);
252         libntfs_directory->inode=inode;
253         libntfs_directory->file_info_list=NULL;
254
255         *method_handle=(GnomeVFSMethodHandle *)libntfs_directory;
256         return errvfsresult;
257 }
258
259
260 static GnomeVFSResult libntfs_gnomevfs_close_directory(GnomeVFSMethod *method,
261                 GnomeVFSMethodHandle *method_handle,GnomeVFSContext *context)
262 {
263 struct libntfs_directory *libntfs_directory;
264 int errint;
265
266         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
267         libntfs_directory=(struct libntfs_directory *)method_handle;
268         g_return_val_if_fail(libntfs_directory!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
269
270         G_LOCK(libntfs);
271         errint=ntfs_inode_close(libntfs_directory->inode);
272         G_UNLOCK(libntfs);
273         if (errint)
274                 g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL);
275
276         if (libntfs_directory->file_info_list) {
277 GList *last_l;
278
279                 /* Prevent gnome_vfs_file_info_list_free() and its gnome_vfs_file_info_unref()
280                  * on the last 'file_info_list' items as it is EOF with NULL '->data'.
281                  */
282                 last_l=g_list_last(libntfs_directory->file_info_list);
283                 g_assert(last_l->data==NULL);
284                 libntfs_directory->file_info_list=g_list_delete_link(libntfs_directory->file_info_list,last_l);
285                 gnome_vfs_file_info_list_free(libntfs_directory->file_info_list);
286                 }
287
288         g_free(libntfs_directory);
289
290         return GNOME_VFS_OK;
291 }
292
293
294 static gchar *libntfs_uchar_to_utf8(const uchar_t *name,const int name_len)
295 {
296 GString *gstring;
297 int i;
298
299         gstring=g_string_sized_new(name_len);
300         for (i=0;i<name_len;i++)
301                 gstring=g_string_append_unichar(gstring,name[i]);
302         return g_string_free(gstring,   /* returns utf8-formatted string */
303                         FALSE); /* free_segment */
304 }
305
306 /* Do not lock 'libntfs' here as we are already locked
307  * inside ntfs_readdir().
308  */
309 static int libntfs_gnomevfs_read_directory_filldir(struct libntfs_directory *libntfs_directory /* dirent */,
310                 const uchar_t *name,const int name_len,const int name_type,const s64 pos,const MFT_REF mref,const unsigned dt_type)
311 {
312 GnomeVFSFileInfo *file_info;
313
314         g_return_val_if_fail(libntfs_directory!=NULL,-1);
315         g_return_val_if_fail(name!=NULL,-1);
316         g_return_val_if_fail(name_len>=0,-1);
317         g_return_val_if_fail(pos>=0,-1);
318
319         if (name_len>0 && name[0]=='$') /* system directory; FIXME: What is its proper identification? */
320                 return 0;       /* continue traversal */
321
322         file_info=gnome_vfs_file_info_new();
323         file_info->name=libntfs_uchar_to_utf8(name,name_len);
324         file_info->valid_fields=0;
325
326         switch (dt_type) {
327                 case NTFS_DT_FIFO: file_info->type=GNOME_VFS_FILE_TYPE_FIFO;             break;
328                 case NTFS_DT_CHR:  file_info->type=GNOME_VFS_FILE_TYPE_CHARACTER_DEVICE; break;
329                 case NTFS_DT_DIR:  file_info->type=GNOME_VFS_FILE_TYPE_DIRECTORY;        break;
330                 case NTFS_DT_BLK:  file_info->type=GNOME_VFS_FILE_TYPE_BLOCK_DEVICE;     break;
331                 case NTFS_DT_REG:  file_info->type=GNOME_VFS_FILE_TYPE_REGULAR;          break;
332                 case NTFS_DT_LNK:  file_info->type=GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK;    break;
333                 case NTFS_DT_SOCK: file_info->type=GNOME_VFS_FILE_TYPE_SOCKET;           break;
334                 /* FIXME: What is 'NTFS_DT_WHT'? */
335                 default: file_info->type=GNOME_VFS_FILE_TYPE_UNKNOWN;
336                 }
337         if (file_info->type!=GNOME_VFS_FILE_TYPE_UNKNOWN)
338                 file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_TYPE;
339
340         /* Detect 'file_info->size': */
341         if (file_info->type==GNOME_VFS_FILE_TYPE_REGULAR) {
342 ntfs_inode *inode;
343
344                 inode=ntfs_inode_open(libntfs_directory->inode->vol,mref);
345                 /* FIXME: Check failed 'inode' open. */
346                 if (inode) {
347 ntfs_attr *attr;
348 int errint;
349
350                         attr=ntfs_attr_open(
351                                         inode,  /* ni */
352                                         AT_DATA,        /* type */
353                                         NULL,   /* name */
354                                         0);     /* name_len */
355                         /* FIXME: Check failed 'attr' open. */
356                         if (attr) {
357                                 file_info->size=attr->data_size;        /* FIXME: Is 'data_size' the right field? */
358                                 file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_SIZE;
359                                 ntfs_attr_close(attr);
360                                 }
361                         errint=ntfs_inode_close(inode);
362                         /* FIXME: Check 'errint'. */
363                         }
364                 }
365
366         libntfs_directory->file_info_list=g_list_prepend(libntfs_directory->file_info_list,file_info);
367
368         return 0;       /* continue traversal */
369 }
370
371
372 static GnomeVFSResult libntfs_gnomevfs_read_directory(GnomeVFSMethod *method,
373                 GnomeVFSMethodHandle *method_handle,GnomeVFSFileInfo *file_info,GnomeVFSContext *context)
374 {
375 GnomeVFSResult errvfsresult;
376 struct libntfs_directory *libntfs_directory;
377
378         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
379         libntfs_directory=(struct libntfs_directory *)method_handle;
380         g_return_val_if_fail(libntfs_directory!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
381         g_return_val_if_fail(file_info!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
382
383         if (!libntfs_directory->file_info_list) {
384 int errint;
385 s64 pos;
386
387                 pos=0;  /* read from the start; incl. "." and ".." entries */
388                 G_LOCK(libntfs);
389                 errint=ntfs_readdir(
390                                 libntfs_directory->inode,       /* dir_ni */
391                                 &pos,   /* pos */
392                                 libntfs_directory,      /* dirent */
393                                 (ntfs_filldir_t)libntfs_gnomevfs_read_directory_filldir);       /* filldir */
394                 G_UNLOCK(libntfs);
395                 if (errint)
396                         return GNOME_VFS_ERROR_INTERNAL;
397
398                 libntfs_directory->file_info_list=g_list_prepend(libntfs_directory->file_info_list,NULL);       /* EOF */
399                 libntfs_directory->file_info_list=g_list_reverse(libntfs_directory->file_info_list);
400                 }
401
402         if (!libntfs_directory->file_info_list->data) {
403                 g_assert(libntfs_directory->file_info_list->next==NULL);
404                 /* Do not clear the list to leave us stuck at EOF - GnomeVFS behaves that way. */
405                 errvfsresult=GNOME_VFS_ERROR_EOF;
406                 }
407         else {
408                 /* Cut first list item. */
409                 gnome_vfs_file_info_copy(
410                                 file_info,      /* dest */
411                                 libntfs_directory->file_info_list->data);       /* src */
412                 gnome_vfs_file_info_unref(libntfs_directory->file_info_list->data);
413                 libntfs_directory->file_info_list=g_list_delete_link(
414                                 libntfs_directory->file_info_list,libntfs_directory->file_info_list);
415                 errvfsresult=GNOME_VFS_OK;
416                 }
417         return errvfsresult;
418 }
419
420
421 struct libntfs_file {
422         ntfs_inode *inode;
423         ntfs_attr *attr;
424         s64 pos;
425         };
426
427 static GnomeVFSResult libntfs_open_attr(struct libntfs_file *libntfs_file)
428 {
429         g_return_val_if_fail(libntfs_file!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
430         g_return_val_if_fail(libntfs_file->inode!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
431
432         if (!libntfs_file->attr) {
433                 G_LOCK(libntfs);
434                 libntfs_file->attr=ntfs_attr_open(
435                                 libntfs_file->inode,    /* ni */
436                                 AT_DATA,        /* type */
437                                 NULL,   /* name */
438                                 0);     /* name_len */
439                 G_UNLOCK(libntfs);
440                 if (!libntfs_file->attr)
441                         return GNOME_VFS_ERROR_BAD_FILE;
442                 libntfs_file->pos=0;
443                 }
444
445         return GNOME_VFS_OK;
446 }
447
448 static GnomeVFSResult libntfs_gnomevfs_open(GnomeVFSMethod *method,
449                 GnomeVFSMethodHandle **method_handle_return,GnomeVFSURI *uri,GnomeVFSOpenMode mode,GnomeVFSContext *context)
450 {
451 GnomeVFSResult errvfsresult;
452 ntfs_volume *volume;
453 ntfs_inode *inode;
454 struct libntfs_file *libntfs_file;
455
456         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
457         g_return_val_if_fail(method_handle_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
458
459         if (GNOME_VFS_OK!=(errvfsresult=libntfs_gnomevfs_uri_parent_init(&volume,uri)))
460                 return errvfsresult;
461
462         if (mode & GNOME_VFS_OPEN_WRITE)
463                 return GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM;
464
465         if (GNOME_VFS_OK!=(errvfsresult=inode_open_by_pathname(&inode,volume,uri->text)))
466                 return errvfsresult;
467
468         libntfs_new(libntfs_file);
469         libntfs_file->inode=inode;
470         libntfs_file->attr=NULL;
471
472         *method_handle_return=(GnomeVFSMethodHandle *)libntfs_file;
473         return errvfsresult;
474 }
475
476
477 static GnomeVFSResult libntfs_gnomevfs_create(GnomeVFSMethod *method,
478                 GnomeVFSMethodHandle **method_handle_return,GnomeVFSURI *uri,GnomeVFSOpenMode mode,gboolean exclusive,guint perm,
479                 GnomeVFSContext *context)
480 {
481 GnomeVFSResult errvfsresult;
482 ntfs_volume *volume;
483
484         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
485         g_return_val_if_fail(method_handle_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
486
487         if (GNOME_VFS_OK!=(errvfsresult=libntfs_gnomevfs_uri_parent_init(&volume,uri)))
488                 return errvfsresult;
489
490         return GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM;
491 }
492
493
494 static GnomeVFSResult libntfs_gnomevfs_close(GnomeVFSMethod *method,
495                 GnomeVFSMethodHandle *method_handle,GnomeVFSContext *context)
496 {
497 struct libntfs_file *libntfs_file;
498 int errint;
499
500         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
501         libntfs_file=(struct libntfs_file *)method_handle;
502         g_return_val_if_fail(libntfs_file!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
503
504         if (libntfs_file->attr) {
505                 G_LOCK(libntfs);
506                 ntfs_attr_close(libntfs_file->attr);
507                 G_UNLOCK(libntfs);
508                 }
509         G_LOCK(libntfs);
510         errint=ntfs_inode_close(libntfs_file->inode);
511         G_UNLOCK(libntfs);
512         if (errint)
513                 g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL);
514
515         g_free(libntfs_file);
516
517         return GNOME_VFS_OK;
518 }
519
520
521 static GnomeVFSResult libntfs_gnomevfs_read(GnomeVFSMethod *method,GnomeVFSMethodHandle *method_handle,
522                 gpointer buffer,GnomeVFSFileSize num_bytes,GnomeVFSFileSize *bytes_read_return,GnomeVFSContext *context)
523 {
524 GnomeVFSResult errvfsresult;
525 struct libntfs_file *libntfs_file;
526 s64 count_s64,got;
527
528         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
529         libntfs_file=(struct libntfs_file *)method_handle;
530         g_return_val_if_fail(libntfs_file!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
531         g_return_val_if_fail(buffer!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
532         g_return_val_if_fail(bytes_read_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
533
534         if (GNOME_VFS_OK!=(errvfsresult=libntfs_open_attr(libntfs_file)))
535                 return errvfsresult;
536
537         count_s64=num_bytes;
538         g_assert((GnomeVFSFileSize)count_s64==num_bytes);
539         G_LOCK(libntfs);
540         got=ntfs_attr_pread(libntfs_file->attr,libntfs_file->pos,count_s64,buffer);
541         G_UNLOCK(libntfs);
542         if (got==-1)
543                 return GNOME_VFS_ERROR_IO;
544
545         libntfs_file->pos+=got;
546         *bytes_read_return=got;
547         g_assert((s64)*bytes_read_return==got);
548
549         return GNOME_VFS_OK;
550 }
551
552
553 static GnomeVFSResult libntfs_gnomevfs_seek(GnomeVFSMethod *method,
554                 GnomeVFSMethodHandle *method_handle,GnomeVFSSeekPosition whence,GnomeVFSFileOffset offset,GnomeVFSContext *context)
555 {
556 GnomeVFSResult errvfsresult;
557 struct libntfs_file *libntfs_file;
558
559         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
560         libntfs_file=(struct libntfs_file *)method_handle;
561         g_return_val_if_fail(libntfs_file!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
562
563         if (GNOME_VFS_OK!=(errvfsresult=libntfs_open_attr(libntfs_file)))
564                 return errvfsresult;
565
566         switch (whence) {
567                 case GNOME_VFS_SEEK_START:
568                         libntfs_file->pos=offset;
569                         break;
570                 case GNOME_VFS_SEEK_CURRENT:
571                         libntfs_file->pos+=offset;
572                         break;
573                 case GNOME_VFS_SEEK_END:
574                         g_return_val_if_reached(GNOME_VFS_ERROR_BAD_PARAMETERS);        /* FIXME: NOT IMPLEMENTED YET */
575                 default: g_assert_not_reached();
576                 }
577
578         return GNOME_VFS_OK;
579 }
580
581 static GnomeVFSResult libntfs_gnomevfs_tell(GnomeVFSMethod *method,
582                 GnomeVFSMethodHandle *method_handle,GnomeVFSFileOffset *offset_return)
583 {
584 GnomeVFSResult errvfsresult;
585 struct libntfs_file *libntfs_file;
586
587         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
588         libntfs_file=(struct libntfs_file *)method_handle;
589         g_return_val_if_fail(libntfs_file!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
590         g_return_val_if_fail(offset_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
591
592         if (GNOME_VFS_OK!=(errvfsresult=libntfs_open_attr(libntfs_file)))
593                 return errvfsresult;
594
595         *offset_return=libntfs_file->pos;
596         g_assert(*offset_return==libntfs_file->pos);
597
598         return errvfsresult;
599 }
600
601
602 static gboolean libntfs_gnomevfs_is_local(GnomeVFSMethod *method,const GnomeVFSURI *uri)
603 {
604         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
605         g_return_val_if_fail(uri!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
606
607         return gnome_vfs_uri_is_local(uri->parent);
608 }
609
610
611 GnomeVFSResult libntfs_gnomevfs_get_file_info_from_handle(GnomeVFSMethod *method,
612                 GnomeVFSMethodHandle *method_handle,GnomeVFSFileInfo *file_info,GnomeVFSFileInfoOptions options,GnomeVFSContext *context)
613 {
614 GnomeVFSResult errvfsresult;
615 struct libntfs_file *libntfs_file;
616
617         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
618         libntfs_file=(struct libntfs_file *)method_handle;
619         g_return_val_if_fail(libntfs_file!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
620         g_return_val_if_fail(file_info!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
621         /* handle 'options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE'? */
622
623         file_info->valid_fields=0;
624         file_info->name=NULL;   /* FIXME: It is complicated to read filename of open 'ntfs_inode'. */
625
626         if (GNOME_VFS_OK!=(errvfsresult=libntfs_open_attr(libntfs_file))) {
627                 /* Assume we are directory: */
628                 file_info->type=GNOME_VFS_FILE_TYPE_DIRECTORY;
629                 /* Do not: file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_TYPE;
630                  * as gnome-vfs-xfer.c/copy_items() does not check 'GNOME_VFS_FILE_INFO_FIELDS_TYPE'
631                  * and we are just bluffing we know it.
632                  */
633                 return GNOME_VFS_OK;
634                 }
635
636         file_info->size=libntfs_file->attr->data_size;  /* FIXME: Is 'data_size' the right field? */
637         file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_SIZE;
638
639         /* FIXME: We do not really know the type of 'libntfs_file'
640          * but gnome-vfs-xfer.c/copy_items() requires 'GNOME_VFS_FILE_TYPE_REGULAR'
641          * to copy it.
642          */
643         file_info->type=GNOME_VFS_FILE_TYPE_REGULAR;
644         /* Do not: file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_TYPE;
645          * as gnome-vfs-xfer.c/copy_items() does not check 'GNOME_VFS_FILE_INFO_FIELDS_TYPE'
646          * and we are just bluffing we know it.
647          */
648
649         return errvfsresult;
650 }
651
652
653 static GnomeVFSResult libntfs_gnomevfs_get_file_info(GnomeVFSMethod *method,
654                 GnomeVFSURI *uri,GnomeVFSFileInfo *file_info,GnomeVFSFileInfoOptions options,GnomeVFSContext *context)
655 {
656 GnomeVFSResult errvfsresult;
657 GnomeVFSMethodHandle *method_handle;
658
659         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
660         g_return_val_if_fail(file_info!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
661         /* handle 'options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE'? */
662
663         if (GNOME_VFS_OK!=(errvfsresult=libntfs_gnomevfs_open(method,&method_handle,uri,GNOME_VFS_OPEN_READ,context)))
664                 return errvfsresult;
665         if (GNOME_VFS_OK!=(errvfsresult=libntfs_gnomevfs_get_file_info_from_handle(method,method_handle,file_info,options,context)))
666                 return errvfsresult;
667         if (GNOME_VFS_OK!=(errvfsresult=libntfs_gnomevfs_close(method,method_handle,context)))
668                 return errvfsresult;
669
670         return GNOME_VFS_OK;
671 }
672
673
674 GnomeVFSResult libntfs_gnomevfs_check_same_fs(GnomeVFSMethod *method,
675                 GnomeVFSURI *a,GnomeVFSURI *b,gboolean *same_fs_return,GnomeVFSContext *context)
676 {
677 ntfs_volume *volume_a;
678 ntfs_volume *volume_b;
679 GnomeVFSResult errvfsresult;
680
681         g_return_val_if_fail(method==&GnomeVFSMethod_static,GNOME_VFS_ERROR_BAD_PARAMETERS);
682         g_return_val_if_fail(same_fs_return!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
683
684         errvfsresult=libntfs_gnomevfs_uri_parent_init(&volume_a,a);
685         g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
686
687         errvfsresult=libntfs_gnomevfs_uri_parent_init(&volume_b,b);
688         g_return_val_if_fail(errvfsresult==GNOME_VFS_OK,errvfsresult);
689
690         *same_fs_return=(volume_a==volume_b);
691
692         return GNOME_VFS_OK;
693 }
694
695
696 /**
697  * libntfs_gnomevfs_init:
698  *
699  * Returns: Initialized structure of #GnomeVFSMethod with static methods of libntfs-gnomevfs.
700  */
701 GnomeVFSMethod *libntfs_gnomevfs_method_init(const gchar *method_name,const gchar *args)
702 {
703 struct method_name_info *method_name_info;
704
705         g_return_val_if_fail(method_name!=NULL,NULL);
706         /* 'args' may be NULL if not supplied. */
707
708         method_name_hash_init();
709
710         G_LOCK(method_name_hash);
711         method_name_info=g_hash_table_lookup(method_name_hash,method_name);
712         if (method_name_info && strcmp(method_name_info->args,args))
713                 method_name_info=NULL;
714         G_UNLOCK(method_name_hash);
715         if (!method_name_info) {
716                 libntfs_new(method_name_info);
717                 method_name_info->args=g_strdup(args);
718                 G_LOCK(method_name_hash);
719                 g_hash_table_replace(method_name_hash,g_strdup(method_name),method_name_info);
720                 G_UNLOCK(method_name_hash);
721                 }
722
723         G_LOCK(GnomeVFSMethod_static);
724         LIBNTFS_MEMZERO(&GnomeVFSMethod_static);
725         GnomeVFSMethod_static.method_table_size=sizeof(GnomeVFSMethod_static);
726         GnomeVFSMethod_static.open                     =libntfs_gnomevfs_open;  /* mandatory */
727         GnomeVFSMethod_static.create                   =libntfs_gnomevfs_create;        /* mandatory */
728         GnomeVFSMethod_static.close                    =libntfs_gnomevfs_close;
729         GnomeVFSMethod_static.read                     =libntfs_gnomevfs_read;
730         GnomeVFSMethod_static.seek                     =libntfs_gnomevfs_seek;
731         GnomeVFSMethod_static.tell                     =libntfs_gnomevfs_tell;
732         GnomeVFSMethod_static.open_directory           =libntfs_gnomevfs_open_directory;
733         GnomeVFSMethod_static.close_directory          =libntfs_gnomevfs_close_directory;
734         GnomeVFSMethod_static.read_directory           =libntfs_gnomevfs_read_directory;
735         GnomeVFSMethod_static.get_file_info            =libntfs_gnomevfs_get_file_info; /* mandatory */
736         GnomeVFSMethod_static.get_file_info_from_handle=libntfs_gnomevfs_get_file_info_from_handle;
737         GnomeVFSMethod_static.is_local                 =libntfs_gnomevfs_is_local;      /* mandatory */
738         GnomeVFSMethod_static.check_same_fs            =libntfs_gnomevfs_check_same_fs;
739         /* TODO: GnomeVFSMethodFindDirectoryFunc find_directory; */
740         /* TODO: GnomeVFSMethodFileControlFunc file_control; */
741         /* R/W:  GnomeVFSMethodCreateSymbolicLinkFunc create_symbolic_link; */
742         /* R/W:  GnomeVFSMethodMonitorAddFunc monitor_add; */
743         /* R/W:  GnomeVFSMethodMonitorCancelFunc monitor_cancel; */
744         /* R/W:  GnomeVFSMethod_static.write; */
745         /* R/W:  GnomeVFSMethod_static.truncate_handle; */
746         /* R/W:  GnomeVFSMethod_static.make_directory; */
747         /* R/W:  GnomeVFSMethod_static.remove_directory; */
748         /* R/W:  GnomeVFSMethod_static.move; */
749         /* R/W:  GnomeVFSMethod_static.unlink; */
750         /* R/W:  GnomeVFSMethod_static.set_file_info; */
751         /* R/W:  GnomeVFSMethod_static.truncate; */
752         G_UNLOCK(GnomeVFSMethod_static);
753
754         return &GnomeVFSMethod_static;
755 }
756
757
758 /**
759  * libntfs_gnomevfs_method_shutdown:
760  *
761  * Shutdowns libntfs-gnomevfs successfuly flushing all caches.
762  *
763  * Sad note about gnome-vfs-2.1.5 is that it never calls this function. :-)
764  */ 
765 void libntfs_gnomevfs_method_shutdown(void)
766 {
767         uri_parent_string_hash_init();
768         G_LOCK(uri_parent_string_hash);
769         g_hash_table_destroy(uri_parent_string_hash);
770         uri_parent_string_hash=NULL;
771         G_UNLOCK(uri_parent_string_hash);
772
773         method_name_hash_init();
774         G_LOCK(method_name_hash);
775         g_hash_table_destroy(method_name_hash);
776         method_name_hash=NULL;
777         G_UNLOCK(method_name_hash);
778 }