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