Improve GSettings test coverage
[platform/upstream/glib.git] / gio / glocalfile.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33
34 #if HAVE_SYS_STATFS_H
35 #include <sys/statfs.h>
36 #endif
37 #if HAVE_SYS_STATVFS_H
38 #include <sys/statvfs.h>
39 #endif
40 #if HAVE_SYS_VFS_H
41 #include <sys/vfs.h>
42 #elif HAVE_SYS_MOUNT_H
43 #if HAVE_SYS_PARAM_H
44 #include <sys/param.h>
45 #endif
46 #include <sys/mount.h>
47 #endif
48
49 #ifndef O_BINARY
50 #define O_BINARY 0
51 #endif
52
53 #include "gfileattribute.h"
54 #include "glocalfile.h"
55 #include "glocalfileinfo.h"
56 #include "glocalfileenumerator.h"
57 #include "glocalfileinputstream.h"
58 #include "glocalfileoutputstream.h"
59 #include "glocalfileiostream.h"
60 #include "glocaldirectorymonitor.h"
61 #include "glocalfilemonitor.h"
62 #include "gmountprivate.h"
63 #include "gunixmounts.h"
64 #include "gioerror.h"
65 #include <glib/gstdio.h>
66 #include "glibintl.h"
67
68 #ifdef G_OS_WIN32
69 #define _WIN32_WINNT 0x0500
70 #include <windows.h>
71 #include <io.h>
72 #include <direct.h>
73
74 #ifndef FILE_READ_ONLY_VOLUME
75 #define FILE_READ_ONLY_VOLUME           0x00080000
76 #endif
77
78 #ifndef S_ISDIR
79 #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
80 #endif
81 #ifndef S_ISLNK
82 #define S_ISLNK(m) (0)
83 #endif
84 #endif
85
86
87 static void g_local_file_file_iface_init (GFileIface *iface);
88
89 static GFileAttributeInfoList *local_writable_attributes = NULL;
90 static /* GFileAttributeInfoList * */ gsize local_writable_namespaces = 0;
91
92 struct _GLocalFile
93 {
94   GObject parent_instance;
95
96   char *filename;
97 };
98
99 #define g_local_file_get_type _g_local_file_get_type
100 G_DEFINE_TYPE_WITH_CODE (GLocalFile, g_local_file, G_TYPE_OBJECT,
101                          G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
102                                                 g_local_file_file_iface_init))
103
104 static char *find_mountpoint_for (const char *file, dev_t dev);
105
106 static void
107 g_local_file_finalize (GObject *object)
108 {
109   GLocalFile *local;
110
111   local = G_LOCAL_FILE (object);
112
113   g_free (local->filename);
114
115   G_OBJECT_CLASS (g_local_file_parent_class)->finalize (object);
116 }
117
118 static void
119 g_local_file_class_init (GLocalFileClass *klass)
120 {
121   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
122   GFileAttributeInfoList *list;
123
124   gobject_class->finalize = g_local_file_finalize;
125
126   /* Set up attribute lists */
127
128   /* Writable attributes: */
129
130   list = g_file_attribute_info_list_new ();
131
132   g_file_attribute_info_list_add (list,
133                                   G_FILE_ATTRIBUTE_UNIX_MODE,
134                                   G_FILE_ATTRIBUTE_TYPE_UINT32,
135                                   G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
136                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
137   
138 #ifdef HAVE_CHOWN
139   g_file_attribute_info_list_add (list,
140                                   G_FILE_ATTRIBUTE_UNIX_UID,
141                                   G_FILE_ATTRIBUTE_TYPE_UINT32,
142                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
143   g_file_attribute_info_list_add (list,
144                                   G_FILE_ATTRIBUTE_UNIX_GID,
145                                   G_FILE_ATTRIBUTE_TYPE_UINT32,
146                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
147 #endif
148   
149 #ifdef HAVE_SYMLINK
150   g_file_attribute_info_list_add (list,
151                                   G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
152                                   G_FILE_ATTRIBUTE_TYPE_BYTE_STRING,
153                                   0);
154 #endif
155   
156 #ifdef HAVE_UTIMES
157   g_file_attribute_info_list_add (list,
158                                   G_FILE_ATTRIBUTE_TIME_MODIFIED,
159                                   G_FILE_ATTRIBUTE_TYPE_UINT64,
160                                   G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
161                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
162   g_file_attribute_info_list_add (list,
163                                   G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
164                                   G_FILE_ATTRIBUTE_TYPE_UINT32,
165                                   G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
166                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
167   /* When copying, the target file is accessed. Replicating
168    * the source access time does not make sense in this case.
169    */
170   g_file_attribute_info_list_add (list,
171                                   G_FILE_ATTRIBUTE_TIME_ACCESS,
172                                   G_FILE_ATTRIBUTE_TYPE_UINT64,
173                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
174   g_file_attribute_info_list_add (list,
175                                   G_FILE_ATTRIBUTE_TIME_ACCESS_USEC,
176                                   G_FILE_ATTRIBUTE_TYPE_UINT32,
177                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
178 #endif
179
180   local_writable_attributes = list;
181 }
182
183 static void
184 g_local_file_init (GLocalFile *local)
185 {
186 }
187
188
189 static char *
190 canonicalize_filename (const char *filename)
191 {
192   char *canon, *start, *p, *q;
193   char *cwd;
194   int i;
195   
196   if (!g_path_is_absolute (filename))
197     {
198       cwd = g_get_current_dir ();
199       canon = g_build_filename (cwd, filename, NULL);
200       g_free (cwd);
201     }
202   else
203     canon = g_strdup (filename);
204
205   start = (char *)g_path_skip_root (canon);
206
207   if (start == NULL)
208     {
209       /* This shouldn't really happen, as g_get_current_dir() should
210          return an absolute pathname, but bug 573843 shows this is
211          not always happening */
212       g_free (canon);
213       return g_build_filename (G_DIR_SEPARATOR_S, filename, NULL);
214     }
215   
216   /* POSIX allows double slashes at the start to
217    * mean something special (as does windows too).
218    * So, "//" != "/", but more than two slashes
219    * is treated as "/".
220    */
221   i = 0;
222   for (p = start - 1;
223        (p >= canon) &&
224          G_IS_DIR_SEPARATOR (*p);
225        p--)
226     i++;
227   if (i > 2)
228     {
229       i -= 1;
230       start -= i;
231       memmove (start, start+i, strlen (start+i)+1);
232     }
233   
234   p = start;
235   while (*p != 0)
236     {
237       if (p[0] == '.' && (p[1] == 0 || G_IS_DIR_SEPARATOR (p[1])))
238         {
239           memmove (p, p+1, strlen (p+1)+1);
240         }
241       else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || G_IS_DIR_SEPARATOR (p[2])))
242         {
243           q = p + 2;
244           /* Skip previous separator */
245           p = p - 2;
246           if (p < start)
247             p = start;
248           while (p > start && !G_IS_DIR_SEPARATOR (*p))
249             p--;
250           if (G_IS_DIR_SEPARATOR (*p))
251             *p++ = G_DIR_SEPARATOR;
252           memmove (p, q, strlen (q)+1);
253         }
254       else
255         {
256           /* Skip until next separator */
257           while (*p != 0 && !G_IS_DIR_SEPARATOR (*p))
258             p++;
259           
260           if (*p != 0)
261             {
262               /* Canonicalize one separator */
263               *p++ = G_DIR_SEPARATOR;
264             }
265         }
266
267       /* Remove additional separators */
268       q = p;
269       while (*q && G_IS_DIR_SEPARATOR (*q))
270         q++;
271
272       if (p != q)
273         memmove (p, q, strlen (q)+1);
274     }
275
276   /* Remove trailing slashes */
277   if (p > start && G_IS_DIR_SEPARATOR (*(p-1)))
278     *(p-1) = 0;
279   
280   return canon;
281 }
282
283 GFile *
284 _g_local_file_new (const char *filename)
285 {
286   GLocalFile *local;
287
288   local = g_object_new (G_TYPE_LOCAL_FILE, NULL);
289   local->filename = canonicalize_filename (filename);
290   
291   return G_FILE (local);
292 }
293
294 static gboolean
295 g_local_file_is_native (GFile *file)
296 {
297   return TRUE;
298 }
299
300 static gboolean
301 g_local_file_has_uri_scheme (GFile      *file,
302                              const char *uri_scheme)
303 {
304   return g_ascii_strcasecmp (uri_scheme, "file") == 0;
305 }
306
307 static char *
308 g_local_file_get_uri_scheme (GFile *file)
309 {
310   return g_strdup ("file");
311 }
312
313 static char *
314 g_local_file_get_basename (GFile *file)
315 {
316   return g_path_get_basename (G_LOCAL_FILE (file)->filename);
317 }
318
319 static char *
320 g_local_file_get_path (GFile *file)
321 {
322   return g_strdup (G_LOCAL_FILE (file)->filename);
323 }
324
325 static char *
326 g_local_file_get_uri (GFile *file)
327 {
328   return g_filename_to_uri (G_LOCAL_FILE (file)->filename, NULL, NULL);
329 }
330
331 static gboolean
332 get_filename_charset (const gchar **filename_charset)
333 {
334   const gchar **charsets;
335   gboolean is_utf8;
336   
337   is_utf8 = g_get_filename_charsets (&charsets);
338
339   if (filename_charset)
340     *filename_charset = charsets[0];
341   
342   return is_utf8;
343 }
344
345 static gboolean
346 name_is_valid_for_display (const char *string,
347                            gboolean    is_valid_utf8)
348 {
349   char c;
350
351   if (!is_valid_utf8 &&
352       !g_utf8_validate (string, -1, NULL))
353     return FALSE;
354
355   while ((c = *string++) != 0)
356     {
357       if (g_ascii_iscntrl (c))
358         return FALSE;
359     }
360
361   return TRUE;
362 }
363
364 static char *
365 g_local_file_get_parse_name (GFile *file)
366 {
367   const char *filename;
368   char *parse_name;
369   const gchar *charset;
370   char *utf8_filename;
371   char *roundtripped_filename;
372   gboolean free_utf8_filename;
373   gboolean is_valid_utf8;
374   char *escaped_path;
375   
376   filename = G_LOCAL_FILE (file)->filename;
377   if (get_filename_charset (&charset))
378     {
379       utf8_filename = (char *)filename;
380       free_utf8_filename = FALSE;
381       is_valid_utf8 = FALSE; /* Can't guarantee this */
382     }
383   else
384     {
385       utf8_filename = g_convert (filename, -1, 
386                                  "UTF-8", charset, NULL, NULL, NULL);
387       free_utf8_filename = TRUE;
388       is_valid_utf8 = TRUE;
389
390       if (utf8_filename != NULL)
391         {
392           /* Make sure we can roundtrip: */
393           roundtripped_filename = g_convert (utf8_filename, -1,
394                                              charset, "UTF-8", NULL, NULL, NULL);
395           
396           if (roundtripped_filename == NULL ||
397               strcmp (filename, roundtripped_filename) != 0)
398             {
399               g_free (utf8_filename);
400               utf8_filename = NULL;
401             }
402
403           g_free (roundtripped_filename);
404         }
405     }
406
407   if (utf8_filename != NULL &&
408       name_is_valid_for_display (utf8_filename, is_valid_utf8))
409     {
410       if (free_utf8_filename)
411         parse_name = utf8_filename;
412       else
413         parse_name = g_strdup (utf8_filename);
414     }
415   else
416     {
417 #ifdef G_OS_WIN32
418       char *dup_filename, *p, *backslash;
419
420       /* Turn backslashes into forward slashes like
421        * g_filename_to_uri() would do (but we can't use that because
422        * it doesn't output IRIs).
423        */
424       dup_filename = g_strdup (filename);
425       filename = p = dup_filename;
426
427       while ((backslash = strchr (p, '\\')) != NULL)
428         {
429           *backslash = '/';
430           p = backslash + 1;
431         }
432 #endif
433
434       escaped_path = g_uri_escape_string (filename,
435                                           G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT "/",
436                                           TRUE);
437       parse_name = g_strconcat ("file://",
438                                 (*escaped_path != '/') ? "/" : "",
439                                 escaped_path,
440                                 NULL);
441       
442       g_free (escaped_path);
443 #ifdef G_OS_WIN32
444       g_free (dup_filename);
445 #endif
446       if (free_utf8_filename)
447         g_free (utf8_filename);
448     }
449   
450   return parse_name;
451 }
452
453 static GFile *
454 g_local_file_get_parent (GFile *file)
455 {
456   GLocalFile *local = G_LOCAL_FILE (file);
457   const char *non_root;
458   char *dirname;
459   GFile *parent;
460
461   /* Check for root */
462   non_root = g_path_skip_root (local->filename);
463   if (*non_root == 0)
464     return NULL;
465
466   dirname = g_path_get_dirname (local->filename);
467   parent = _g_local_file_new (dirname);
468   g_free (dirname);
469   return parent;
470 }
471
472 static GFile *
473 g_local_file_dup (GFile *file)
474 {
475   GLocalFile *local = G_LOCAL_FILE (file);
476
477   return _g_local_file_new (local->filename);
478 }
479
480 static guint
481 g_local_file_hash (GFile *file)
482 {
483   GLocalFile *local = G_LOCAL_FILE (file);
484   
485   return g_str_hash (local->filename);
486 }
487
488 static gboolean
489 g_local_file_equal (GFile *file1,
490                     GFile *file2)
491 {
492   GLocalFile *local1 = G_LOCAL_FILE (file1);
493   GLocalFile *local2 = G_LOCAL_FILE (file2);
494
495   return g_str_equal (local1->filename, local2->filename);
496 }
497
498 static const char *
499 match_prefix (const char *path, 
500               const char *prefix)
501 {
502   int prefix_len;
503
504   prefix_len = strlen (prefix);
505   if (strncmp (path, prefix, prefix_len) != 0)
506     return NULL;
507   
508   /* Handle the case where prefix is the root, so that
509    * the IS_DIR_SEPRARATOR check below works */
510   if (prefix_len > 0 &&
511       G_IS_DIR_SEPARATOR (prefix[prefix_len-1]))
512     prefix_len--;
513   
514   return path + prefix_len;
515 }
516
517 static gboolean
518 g_local_file_prefix_matches (GFile *parent,
519                              GFile *descendant)
520 {
521   GLocalFile *parent_local = G_LOCAL_FILE (parent);
522   GLocalFile *descendant_local = G_LOCAL_FILE (descendant);
523   const char *remainder;
524
525   remainder = match_prefix (descendant_local->filename, parent_local->filename);
526   if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
527     return TRUE;
528   return FALSE;
529 }
530
531 static char *
532 g_local_file_get_relative_path (GFile *parent,
533                                 GFile *descendant)
534 {
535   GLocalFile *parent_local = G_LOCAL_FILE (parent);
536   GLocalFile *descendant_local = G_LOCAL_FILE (descendant);
537   const char *remainder;
538
539   remainder = match_prefix (descendant_local->filename, parent_local->filename);
540   
541   if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
542     return g_strdup (remainder + 1);
543   return NULL;
544 }
545
546 static GFile *
547 g_local_file_resolve_relative_path (GFile      *file,
548                                     const char *relative_path)
549 {
550   GLocalFile *local = G_LOCAL_FILE (file);
551   char *filename;
552   GFile *child;
553
554   if (g_path_is_absolute (relative_path))
555     return _g_local_file_new (relative_path);
556   
557   filename = g_build_filename (local->filename, relative_path, NULL);
558   child = _g_local_file_new (filename);
559   g_free (filename);
560   
561   return child;
562 }
563
564 static GFileEnumerator *
565 g_local_file_enumerate_children (GFile                *file,
566                                  const char           *attributes,
567                                  GFileQueryInfoFlags   flags,
568                                  GCancellable         *cancellable,
569                                  GError              **error)
570 {
571   GLocalFile *local = G_LOCAL_FILE (file);
572   return _g_local_file_enumerator_new (local,
573                                        attributes, flags,
574                                        cancellable, error);
575 }
576
577 static GFile *
578 g_local_file_get_child_for_display_name (GFile        *file,
579                                          const char   *display_name,
580                                          GError      **error)
581 {
582   GFile *new_file;
583   char *basename;
584
585   basename = g_filename_from_utf8 (display_name, -1, NULL, NULL, NULL);
586   if (basename == NULL)
587     {
588       g_set_error (error, G_IO_ERROR,
589                    G_IO_ERROR_INVALID_FILENAME,
590                    _("Invalid filename %s"), display_name);
591       return NULL;
592     }
593
594   new_file = g_file_get_child (file, basename);
595   g_free (basename);
596   
597   return new_file;
598 }
599
600 #if defined(USE_STATFS) && !defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
601 static const char *
602 get_fs_type (long f_type)
603 {
604   /* filesystem ids taken from linux manpage */
605   switch (f_type) 
606     {
607     case 0xadf5:
608       return "adfs";
609     case 0x5346414f:
610       return "afs";
611     case 0x0187:
612       return "autofs";
613     case 0xADFF:
614       return "affs";
615     case 0x42465331:
616       return "befs";
617     case 0x1BADFACE:
618       return "bfs";
619     case 0x9123683E:
620       return "btrfs";
621     case 0xFF534D42:
622       return "cifs";
623     case 0x73757245:
624       return "coda";
625     case 0x012FF7B7:
626       return "coh";
627     case 0x28cd3d45:
628       return "cramfs";
629     case 0x1373:
630       return "devfs";
631     case 0x00414A53:
632       return "efs";
633     case 0x137D:
634       return "ext";
635     case 0xEF51:
636       return "ext2";
637     case 0xEF53:
638       return "ext3/ext4";
639     case 0x4244:
640       return "hfs";
641     case 0xF995E849:
642       return "hpfs";
643     case 0x958458f6:
644       return "hugetlbfs";
645     case 0x9660:
646       return "isofs";
647     case 0x72b6:
648       return "jffs2";
649     case 0x3153464a:
650       return "jfs";
651     case 0x137F:
652       return "minix";
653     case 0x138F:
654       return "minix2";
655     case 0x2468:
656       return "minix2";
657     case 0x2478:
658       return "minix22";
659     case 0x4d44:
660       return "msdos";
661     case 0x564c:
662       return "ncp";
663     case 0x6969:
664       return "nfs";
665     case 0x5346544e:
666       return "ntfs";
667     case 0x9fa1:
668       return "openprom";
669     case 0x9fa0:
670       return "proc";
671     case 0x002f:
672       return "qnx4";
673     case 0x52654973:
674       return "reiserfs";
675     case 0x7275:
676       return "romfs";
677     case 0x517B:
678       return "smb";
679     case 0x73717368:
680       return "squashfs";
681     case 0x012FF7B6:
682       return "sysv2";
683     case 0x012FF7B5:
684       return "sysv4";
685     case 0x01021994:
686       return "tmpfs";
687     case 0x15013346:
688       return "udf";
689     case 0x00011954:
690       return "ufs";
691     case 0x9fa2:
692       return "usbdevice";
693     case 0xa501FCF5:
694       return "vxfs";
695     case 0x012FF7B4:
696       return "xenix";
697     case 0x58465342:
698       return "xfs";
699     case 0x012FD16D:
700       return "xiafs";
701     case 0x52345362:
702       return "reiser4";
703     default:
704       return NULL;
705     }
706 }
707 #endif
708
709 #ifndef G_OS_WIN32
710
711 G_LOCK_DEFINE_STATIC(mount_info_hash);
712 static GHashTable *mount_info_hash = NULL;
713 static guint64 mount_info_hash_cache_time = 0;
714
715 typedef enum {
716   MOUNT_INFO_READONLY = 1<<0
717 } MountInfo;
718
719 static gboolean
720 device_equal (gconstpointer v1,
721               gconstpointer v2)
722 {
723   return *(dev_t *)v1 == *(dev_t *)v2;
724 }
725
726 static guint
727 device_hash (gconstpointer v)
728 {
729   return (guint) *(dev_t *)v;
730 }
731
732 static void
733 get_mount_info (GFileInfo             *fs_info,
734                 const char            *path,
735                 GFileAttributeMatcher *matcher)
736 {
737   GStatBuf buf;
738   gboolean got_info;
739   gpointer info_as_ptr;
740   guint mount_info;
741   char *mountpoint;
742   dev_t *dev;
743   GUnixMountEntry *mount;
744   guint64 cache_time;
745
746   if (g_lstat (path, &buf) != 0)
747     return;
748
749   G_LOCK (mount_info_hash);
750
751   if (mount_info_hash == NULL)
752     mount_info_hash = g_hash_table_new_full (device_hash, device_equal,
753                                              g_free, NULL);
754
755
756   if (g_unix_mounts_changed_since (mount_info_hash_cache_time))
757     g_hash_table_remove_all (mount_info_hash);
758   
759   got_info = g_hash_table_lookup_extended (mount_info_hash,
760                                            &buf.st_dev,
761                                            NULL,
762                                            &info_as_ptr);
763   
764   G_UNLOCK (mount_info_hash);
765   
766   mount_info = GPOINTER_TO_UINT (info_as_ptr);
767   
768   if (!got_info)
769     {
770       mount_info = 0;
771
772       mountpoint = find_mountpoint_for (path, buf.st_dev);
773       if (mountpoint == NULL)
774         mountpoint = g_strdup ("/");
775
776       mount = g_unix_mount_at (mountpoint, &cache_time);
777       if (mount)
778         {
779           if (g_unix_mount_is_readonly (mount))
780             mount_info |= MOUNT_INFO_READONLY;
781           
782           g_unix_mount_free (mount);
783         }
784
785       g_free (mountpoint);
786
787       dev = g_new0 (dev_t, 1);
788       *dev = buf.st_dev;
789       
790       G_LOCK (mount_info_hash);
791       mount_info_hash_cache_time = cache_time;
792       g_hash_table_insert (mount_info_hash, dev, GUINT_TO_POINTER (mount_info));
793       G_UNLOCK (mount_info_hash);
794     }
795
796   if (mount_info & MOUNT_INFO_READONLY)
797     g_file_info_set_attribute_boolean (fs_info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
798 }
799
800 #endif
801
802 #ifdef G_OS_WIN32
803
804 static gboolean
805 is_xp_or_later (void)
806 {
807   static int result = -1;
808
809   if (result == -1)
810     {
811 #ifndef _MSC_VER    
812       OSVERSIONINFOEX ver_info = {0};
813       DWORDLONG cond_mask = 0;
814       int op = VER_GREATER_EQUAL;
815
816       ver_info.dwOSVersionInfoSize = sizeof ver_info;
817       ver_info.dwMajorVersion = 5;
818       ver_info.dwMinorVersion = 1;
819
820       VER_SET_CONDITION (cond_mask, VER_MAJORVERSION, op);
821       VER_SET_CONDITION (cond_mask, VER_MINORVERSION, op);
822
823       result = VerifyVersionInfo (&ver_info,
824                                   VER_MAJORVERSION | VER_MINORVERSION, 
825                                   cond_mask) != 0;
826 #else
827       result = ((DWORD)(LOBYTE (LOWORD (GetVersion ())))) >= 5;  
828 #endif
829     }
830
831   return result;
832 }
833
834 static wchar_t *
835 get_volume_for_path (const char *path)
836 {
837   long len;
838   wchar_t *wpath;
839   wchar_t *result;
840
841   wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
842   result = g_new (wchar_t, MAX_PATH);
843
844   if (!GetVolumePathNameW (wpath, result, MAX_PATH))
845     {
846       char *msg = g_win32_error_message (GetLastError ());
847       g_critical ("GetVolumePathName failed: %s", msg);
848       g_free (msg);
849       g_free (result);
850       g_free (wpath);
851       return NULL;
852     }
853
854   len = wcslen (result);
855   if (len > 0 && result[len-1] != L'\\')
856     {
857       result = g_renew (wchar_t, result, len + 2);
858       result[len] = L'\\';
859       result[len + 1] = 0;
860     }
861
862   g_free (wpath);
863   return result;
864 }
865
866 static char *
867 find_mountpoint_for (const char *file, dev_t dev)
868 {
869   wchar_t *wpath;
870   char *utf8_path;
871
872   wpath = get_volume_for_path (file);
873   if (!wpath)
874     return NULL;
875
876   utf8_path = g_utf16_to_utf8 (wpath, -1, NULL, NULL, NULL);
877
878   g_free (wpath);
879   return utf8_path;
880 }
881
882 static void
883 get_filesystem_readonly (GFileInfo  *info,
884                          const char *path)
885 {
886   wchar_t *rootdir;
887
888   rootdir = get_volume_for_path (path);
889
890   if (rootdir)
891     {
892       if (is_xp_or_later ())
893         {
894           DWORD flags;
895           if (GetVolumeInformationW (rootdir, NULL, 0, NULL, NULL, &flags, NULL, 0))
896             g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,
897                                                (flags & FILE_READ_ONLY_VOLUME) != 0);
898         }
899       else
900         {
901           if (GetDriveTypeW (rootdir) == DRIVE_CDROM)
902             g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
903         }
904     }
905
906   g_free (rootdir);
907 }
908
909 #endif /* G_OS_WIN32 */
910
911 static GFileInfo *
912 g_local_file_query_filesystem_info (GFile         *file,
913                                     const char    *attributes,
914                                     GCancellable  *cancellable,
915                                     GError       **error)
916 {
917   GLocalFile *local = G_LOCAL_FILE (file);
918   GFileInfo *info;
919   int statfs_result = 0;
920   gboolean no_size;
921 #ifndef G_OS_WIN32
922   const char *fstype;
923 #ifdef USE_STATFS
924   guint64 block_size;
925   struct statfs statfs_buffer;
926 #elif defined(USE_STATVFS)
927   guint64 block_size;
928   struct statvfs statfs_buffer;
929 #endif /* USE_STATFS */
930 #endif /* G_OS_WIN32 */
931   GFileAttributeMatcher *attribute_matcher;
932         
933   no_size = FALSE;
934   
935 #ifdef USE_STATFS
936   
937 #if STATFS_ARGS == 2
938   statfs_result = statfs (local->filename, &statfs_buffer);
939 #elif STATFS_ARGS == 4
940   statfs_result = statfs (local->filename, &statfs_buffer,
941                           sizeof (statfs_buffer), 0);
942 #endif /* STATFS_ARGS == 2 */
943   block_size = statfs_buffer.f_bsize;
944   
945   /* Many backends can't report free size (for instance the gvfs fuse
946      backend for backend not supporting this), and set f_bfree to 0,
947      but it can be 0 for real too. We treat the available == 0 and
948      free == 0 case as "both of these are invalid".
949    */
950 #ifndef G_OS_WIN32
951   if (statfs_result == 0 &&
952       statfs_buffer.f_bavail == 0 && statfs_buffer.f_bfree == 0)
953     no_size = TRUE;
954 #endif /* G_OS_WIN32 */
955   
956 #elif defined(USE_STATVFS)
957   statfs_result = statvfs (local->filename, &statfs_buffer);
958   block_size = statfs_buffer.f_frsize; 
959 #endif /* USE_STATFS */
960
961   if (statfs_result == -1)
962     {
963       int errsv = errno;
964
965       g_set_error (error, G_IO_ERROR,
966                    g_io_error_from_errno (errsv),
967                    _("Error getting filesystem info: %s"),
968                    g_strerror (errsv));
969       return NULL;
970     }
971
972   info = g_file_info_new ();
973
974   attribute_matcher = g_file_attribute_matcher_new (attributes);
975   
976   if (!no_size &&
977       g_file_attribute_matcher_matches (attribute_matcher,
978                                         G_FILE_ATTRIBUTE_FILESYSTEM_FREE))
979     {
980 #ifdef G_OS_WIN32
981       gchar *localdir = g_path_get_dirname (local->filename);
982       wchar_t *wdirname = g_utf8_to_utf16 (localdir, -1, NULL, NULL, NULL);
983       ULARGE_INTEGER li;
984       
985       g_free (localdir);
986       if (GetDiskFreeSpaceExW (wdirname, &li, NULL, NULL))
987         g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, (guint64)li.QuadPart);
988       g_free (wdirname);
989 #else
990 #if defined(USE_STATFS) || defined(USE_STATVFS)
991       g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, block_size * statfs_buffer.f_bavail);
992 #endif
993 #endif
994     }
995   if (!no_size &&
996       g_file_attribute_matcher_matches (attribute_matcher,
997                                         G_FILE_ATTRIBUTE_FILESYSTEM_SIZE))
998     {
999 #ifdef G_OS_WIN32
1000       gchar *localdir = g_path_get_dirname (local->filename);
1001       wchar_t *wdirname = g_utf8_to_utf16 (localdir, -1, NULL, NULL, NULL);
1002       ULARGE_INTEGER li;
1003       
1004       g_free (localdir);
1005       if (GetDiskFreeSpaceExW (wdirname, NULL, &li, NULL))
1006         g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,  (guint64)li.QuadPart);
1007       g_free (wdirname);
1008 #else
1009 #if defined(USE_STATFS) || defined(USE_STATVFS)
1010       g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, block_size * statfs_buffer.f_blocks);
1011 #endif
1012 #endif /* G_OS_WIN32 */
1013     }
1014
1015   if (!no_size &&
1016       g_file_attribute_matcher_matches (attribute_matcher,
1017                                         G_FILE_ATTRIBUTE_FILESYSTEM_USED))
1018     {
1019 #ifdef G_OS_WIN32
1020       gchar *localdir = g_path_get_dirname (local->filename);
1021       wchar_t *wdirname = g_utf8_to_utf16 (localdir, -1, NULL, NULL, NULL);
1022       ULARGE_INTEGER li_free;
1023       ULARGE_INTEGER li_total;
1024
1025       g_free (localdir);
1026       if (GetDiskFreeSpaceExW (wdirname, &li_free, &li_total, NULL))
1027         g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED,  (guint64)li_total.QuadPart - (guint64)li_free.QuadPart);
1028       g_free (wdirname);
1029 #else
1030       g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED, block_size * (statfs_buffer.f_blocks - statfs_buffer.f_bfree));
1031 #endif /* G_OS_WIN32 */
1032     }
1033
1034 #ifndef G_OS_WIN32
1035 #ifdef USE_STATFS
1036 #if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
1037   fstype = g_strdup (statfs_buffer.f_fstypename);
1038 #else
1039   fstype = get_fs_type (statfs_buffer.f_type);
1040 #endif
1041
1042 #elif defined(USE_STATVFS)
1043 #if defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
1044   fstype = g_strdup (statfs_buffer.f_fstypename);
1045 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
1046   fstype = g_strdup (statfs_buffer.f_basetype);
1047 #else
1048   fstype = NULL;
1049 #endif
1050 #endif /* USE_STATFS */
1051
1052   if (fstype &&
1053       g_file_attribute_matcher_matches (attribute_matcher,
1054                                         G_FILE_ATTRIBUTE_FILESYSTEM_TYPE))
1055     g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, fstype);
1056 #endif /* G_OS_WIN32 */
1057
1058   if (g_file_attribute_matcher_matches (attribute_matcher,
1059                                         G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
1060     {
1061 #ifdef G_OS_WIN32
1062       get_filesystem_readonly (info, local->filename);
1063 #else
1064       get_mount_info (info, local->filename, attribute_matcher);
1065 #endif /* G_OS_WIN32 */
1066     }
1067   
1068   g_file_attribute_matcher_unref (attribute_matcher);
1069   
1070   return info;
1071 }
1072
1073 static GMount *
1074 g_local_file_find_enclosing_mount (GFile         *file,
1075                                    GCancellable  *cancellable,
1076                                    GError       **error)
1077 {
1078   GLocalFile *local = G_LOCAL_FILE (file);
1079   GStatBuf buf;
1080   char *mountpoint;
1081   GMount *mount;
1082
1083   if (g_lstat (local->filename, &buf) != 0)
1084     {
1085       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1086                       /* Translators: This is an error message when trying to
1087                        * find the enclosing (user visible) mount of a file, but
1088                        * none exists. */
1089                       _("Containing mount does not exist"));
1090       return NULL;
1091     }
1092
1093   mountpoint = find_mountpoint_for (local->filename, buf.st_dev);
1094   if (mountpoint == NULL)
1095     {
1096       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1097                       /* Translators: This is an error message when trying to
1098                        * find the enclosing (user visible) mount of a file, but
1099                        * none exists. */
1100                       _("Containing mount does not exist"));
1101       return NULL;
1102     }
1103
1104   mount = _g_mount_get_for_mount_path (mountpoint, cancellable);
1105   g_free (mountpoint);
1106   if (mount)
1107     return mount;
1108
1109   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1110                   /* Translators: This is an error message when trying to find
1111                    * the enclosing (user visible) mount of a file, but none
1112                    * exists. */
1113                   _("Containing mount does not exist"));
1114   return NULL;
1115 }
1116
1117 static GFile *
1118 g_local_file_set_display_name (GFile         *file,
1119                                const char    *display_name,
1120                                GCancellable  *cancellable,
1121                                GError       **error)
1122 {
1123   GLocalFile *local, *new_local;
1124   GFile *new_file, *parent;
1125   GStatBuf statbuf;
1126   GVfsClass *class;
1127   GVfs *vfs;
1128   int errsv;
1129
1130   parent = g_file_get_parent (file);
1131   if (parent == NULL)
1132     {
1133       g_set_error_literal (error, G_IO_ERROR,
1134                            G_IO_ERROR_FAILED,
1135                            _("Can't rename root directory"));
1136       return NULL;
1137     }
1138   
1139   new_file = g_file_get_child_for_display_name (parent, display_name, error);
1140   g_object_unref (parent);
1141   
1142   if (new_file == NULL)
1143     return NULL;
1144   local = G_LOCAL_FILE (file);
1145   new_local = G_LOCAL_FILE (new_file);
1146
1147   if (g_lstat (new_local->filename, &statbuf) == -1) 
1148     {
1149       errsv = errno;
1150
1151       if (errsv != ENOENT)
1152         {
1153           g_set_error (error, G_IO_ERROR,
1154                        g_io_error_from_errno (errsv),
1155                        _("Error renaming file: %s"),
1156                        g_strerror (errsv));
1157           return NULL;
1158         }
1159     }
1160   else
1161     {
1162       g_set_error_literal (error, G_IO_ERROR,
1163                            G_IO_ERROR_EXISTS,
1164                            _("Can't rename file, filename already exists"));
1165       return NULL;
1166     }
1167
1168   if (g_rename (local->filename, new_local->filename) == -1)
1169     {
1170       errsv = errno;
1171
1172       if (errsv == EINVAL)
1173         /* We can't get a rename file into itself error herer,
1174            so this must be an invalid filename, on e.g. FAT */
1175         g_set_error_literal (error, G_IO_ERROR,
1176                              G_IO_ERROR_INVALID_FILENAME,
1177                              _("Invalid filename"));
1178       else
1179         g_set_error (error, G_IO_ERROR,
1180                      g_io_error_from_errno (errsv),
1181                      _("Error renaming file: %s"),
1182                      g_strerror (errsv));
1183       g_object_unref (new_file);
1184       return NULL;
1185     }
1186
1187   vfs = g_vfs_get_default ();
1188   class = G_VFS_GET_CLASS (vfs);
1189   if (class->local_file_moved)
1190     class->local_file_moved (vfs, local->filename, new_local->filename);
1191
1192   return new_file;
1193 }
1194
1195 static GFileInfo *
1196 g_local_file_query_info (GFile                *file,
1197                          const char           *attributes,
1198                          GFileQueryInfoFlags   flags,
1199                          GCancellable         *cancellable,
1200                          GError              **error)
1201 {
1202   GLocalFile *local = G_LOCAL_FILE (file);
1203   GFileInfo *info;
1204   GFileAttributeMatcher *matcher;
1205   char *basename, *dirname;
1206   GLocalParentFileInfo parent_info;
1207
1208   matcher = g_file_attribute_matcher_new (attributes);
1209   
1210   basename = g_path_get_basename (local->filename);
1211   
1212   dirname = g_path_get_dirname (local->filename);
1213   _g_local_file_info_get_parent_info (dirname, matcher, &parent_info);
1214   g_free (dirname);
1215   
1216   info = _g_local_file_info_get (basename, local->filename,
1217                                  matcher, flags, &parent_info,
1218                                  error);
1219   
1220
1221   _g_local_file_info_free_parent_info (&parent_info);
1222   g_free (basename);
1223
1224   g_file_attribute_matcher_unref (matcher);
1225
1226   return info;
1227 }
1228
1229 static GFileAttributeInfoList *
1230 g_local_file_query_settable_attributes (GFile         *file,
1231                                         GCancellable  *cancellable,
1232                                         GError       **error)
1233 {
1234   return g_file_attribute_info_list_ref (local_writable_attributes);
1235 }
1236
1237 static GFileAttributeInfoList *
1238 g_local_file_query_writable_namespaces (GFile         *file,
1239                                         GCancellable  *cancellable,
1240                                         GError       **error)
1241 {
1242   GFileAttributeInfoList *list;
1243   GVfsClass *class;
1244   GVfs *vfs;
1245
1246   if (g_once_init_enter (&local_writable_namespaces))
1247     {
1248       /* Writable namespaces: */
1249
1250       list = g_file_attribute_info_list_new ();
1251
1252 #ifdef HAVE_XATTR
1253       g_file_attribute_info_list_add (list,
1254                                       "xattr",
1255                                       G_FILE_ATTRIBUTE_TYPE_STRING,
1256                                       G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
1257                                       G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
1258       g_file_attribute_info_list_add (list,
1259                                       "xattr-sys",
1260                                       G_FILE_ATTRIBUTE_TYPE_STRING,
1261                                       G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
1262 #endif
1263
1264       vfs = g_vfs_get_default ();
1265       class = G_VFS_GET_CLASS (vfs);
1266       if (class->add_writable_namespaces)
1267         class->add_writable_namespaces (vfs, list);
1268
1269       g_once_init_leave (&local_writable_namespaces, (gsize)list);
1270     }
1271   list = (GFileAttributeInfoList *)local_writable_namespaces;
1272
1273   return g_file_attribute_info_list_ref (list);
1274 }
1275
1276 static gboolean
1277 g_local_file_set_attribute (GFile                *file,
1278                             const char           *attribute,
1279                             GFileAttributeType    type,
1280                             gpointer              value_p,
1281                             GFileQueryInfoFlags   flags,
1282                             GCancellable         *cancellable,
1283                             GError              **error)
1284 {
1285   GLocalFile *local = G_LOCAL_FILE (file);
1286
1287   return _g_local_file_info_set_attribute (local->filename,
1288                                            attribute,
1289                                            type,
1290                                            value_p,
1291                                            flags,
1292                                            cancellable,
1293                                            error);
1294 }
1295
1296 static gboolean
1297 g_local_file_set_attributes_from_info (GFile                *file,
1298                                        GFileInfo            *info,
1299                                        GFileQueryInfoFlags   flags,
1300                                        GCancellable         *cancellable,
1301                                        GError              **error)
1302 {
1303   GLocalFile *local = G_LOCAL_FILE (file);
1304   int res, chained_res;
1305   GFileIface *default_iface;
1306
1307   res = _g_local_file_info_set_attributes (local->filename,
1308                                            info, flags, 
1309                                            cancellable,
1310                                            error);
1311
1312   if (!res)
1313     error = NULL; /* Don't write over error if further errors */
1314
1315   default_iface = g_type_default_interface_peek (G_TYPE_FILE);
1316
1317   chained_res = (default_iface->set_attributes_from_info) (file, info, flags, cancellable, error);
1318   
1319   return res && chained_res;
1320 }
1321
1322 static GFileInputStream *
1323 g_local_file_read (GFile         *file,
1324                    GCancellable  *cancellable,
1325                    GError       **error)
1326 {
1327   GLocalFile *local = G_LOCAL_FILE (file);
1328   int fd, ret;
1329   GLocalFileStat buf;
1330   
1331   fd = g_open (local->filename, O_RDONLY|O_BINARY, 0);
1332   if (fd == -1)
1333     {
1334       int errsv = errno;
1335
1336 #ifdef G_OS_WIN32
1337       if (errsv == EACCES)
1338         {
1339           ret = _stati64 (local->filename, &buf);
1340           if (ret == 0 && S_ISDIR (buf.st_mode))
1341             {
1342               g_set_error_literal (error, G_IO_ERROR,
1343                                    G_IO_ERROR_IS_DIRECTORY,
1344                                    _("Can't open directory"));
1345               return NULL;
1346             }
1347         }
1348 #endif
1349
1350       g_set_error (error, G_IO_ERROR,
1351                    g_io_error_from_errno (errsv),
1352                    _("Error opening file: %s"),
1353                    g_strerror (errsv));
1354       return NULL;
1355     }
1356
1357 #ifdef G_OS_WIN32
1358   ret = _fstati64 (fd, &buf);
1359 #else
1360   ret = fstat (fd, &buf);
1361 #endif
1362
1363   if (ret == 0 && S_ISDIR (buf.st_mode))
1364     {
1365       close (fd);
1366       g_set_error_literal (error, G_IO_ERROR,
1367                            G_IO_ERROR_IS_DIRECTORY,
1368                            _("Can't open directory"));
1369       return NULL;
1370     }
1371   
1372   return _g_local_file_input_stream_new (fd);
1373 }
1374
1375 static GFileOutputStream *
1376 g_local_file_append_to (GFile             *file,
1377                         GFileCreateFlags   flags,
1378                         GCancellable      *cancellable,
1379                         GError           **error)
1380 {
1381   return _g_local_file_output_stream_append (G_LOCAL_FILE (file)->filename,
1382                                              flags, cancellable, error);
1383 }
1384
1385 static GFileOutputStream *
1386 g_local_file_create (GFile             *file,
1387                      GFileCreateFlags   flags,
1388                      GCancellable      *cancellable,
1389                      GError           **error)
1390 {
1391   return _g_local_file_output_stream_create (G_LOCAL_FILE (file)->filename,
1392                                              FALSE,
1393                                              flags, cancellable, error);
1394 }
1395
1396 static GFileOutputStream *
1397 g_local_file_replace (GFile             *file,
1398                       const char        *etag,
1399                       gboolean           make_backup,
1400                       GFileCreateFlags   flags,
1401                       GCancellable      *cancellable,
1402                       GError           **error)
1403 {
1404   return _g_local_file_output_stream_replace (G_LOCAL_FILE (file)->filename,
1405                                               FALSE,
1406                                               etag, make_backup, flags,
1407                                               cancellable, error);
1408 }
1409
1410 static GFileIOStream *
1411 g_local_file_open_readwrite (GFile                      *file,
1412                              GCancellable               *cancellable,
1413                              GError                    **error)
1414 {
1415   GFileOutputStream *output;
1416   GFileIOStream *res;
1417
1418   output = _g_local_file_output_stream_open (G_LOCAL_FILE (file)->filename,
1419                                              TRUE,
1420                                              cancellable, error);
1421   if (output == NULL)
1422     return NULL;
1423
1424   res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output));
1425   g_object_unref (output);
1426   return res;
1427 }
1428
1429 static GFileIOStream *
1430 g_local_file_create_readwrite (GFile                      *file,
1431                                GFileCreateFlags            flags,
1432                                GCancellable               *cancellable,
1433                                GError                    **error)
1434 {
1435   GFileOutputStream *output;
1436   GFileIOStream *res;
1437
1438   output = _g_local_file_output_stream_create (G_LOCAL_FILE (file)->filename,
1439                                                TRUE, flags,
1440                                                cancellable, error);
1441   if (output == NULL)
1442     return NULL;
1443
1444   res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output));
1445   g_object_unref (output);
1446   return res;
1447 }
1448
1449 static GFileIOStream *
1450 g_local_file_replace_readwrite (GFile                      *file,
1451                                 const char                 *etag,
1452                                 gboolean                    make_backup,
1453                                 GFileCreateFlags            flags,
1454                                 GCancellable               *cancellable,
1455                                 GError                    **error)
1456 {
1457   GFileOutputStream *output;
1458   GFileIOStream *res;
1459
1460   output = _g_local_file_output_stream_replace (G_LOCAL_FILE (file)->filename,
1461                                                 TRUE,
1462                                                 etag, make_backup, flags,
1463                                                 cancellable, error);
1464   if (output == NULL)
1465     return NULL;
1466
1467   res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output));
1468   g_object_unref (output);
1469   return res;
1470 }
1471
1472 static gboolean
1473 g_local_file_delete (GFile         *file,
1474                      GCancellable  *cancellable,
1475                      GError       **error)
1476 {
1477   GLocalFile *local = G_LOCAL_FILE (file);
1478   GVfsClass *class;
1479   GVfs *vfs;
1480
1481   if (g_remove (local->filename) == -1)
1482     {
1483       int errsv = errno;
1484
1485       /* Posix allows EEXIST too, but the more sane error
1486          is G_IO_ERROR_NOT_FOUND, and it's what nautilus
1487          expects */
1488       if (errsv == EEXIST)
1489         errsv = ENOTEMPTY;
1490
1491       g_set_error (error, G_IO_ERROR,
1492                    g_io_error_from_errno (errsv),
1493                    _("Error removing file: %s"),
1494                    g_strerror (errsv));
1495       return FALSE;
1496     }
1497
1498   vfs = g_vfs_get_default ();
1499   class = G_VFS_GET_CLASS (vfs);
1500   if (class->local_file_removed)
1501     class->local_file_removed (vfs, local->filename);
1502
1503   return TRUE;
1504 }
1505
1506 #ifndef G_OS_WIN32
1507
1508 static char *
1509 strip_trailing_slashes (const char *path)
1510 {
1511   char *path_copy;
1512   int len;
1513
1514   path_copy = g_strdup (path);
1515   len = strlen (path_copy);
1516   while (len > 1 && path_copy[len-1] == '/')
1517     path_copy[--len] = 0;
1518
1519   return path_copy;
1520  }
1521
1522 static char *
1523 expand_symlink (const char *link)
1524 {
1525   char *resolved, *canonical, *parent, *link2;
1526   char symlink_value[4096];
1527 #ifdef G_OS_WIN32
1528 #else
1529   ssize_t res;
1530 #endif
1531   
1532 #ifdef G_OS_WIN32
1533 #else
1534   res = readlink (link, symlink_value, sizeof (symlink_value) - 1);
1535   
1536   if (res == -1)
1537     return g_strdup (link);
1538   symlink_value[res] = 0;
1539 #endif
1540   
1541   if (g_path_is_absolute (symlink_value))
1542     return canonicalize_filename (symlink_value);
1543   else
1544     {
1545       link2 = strip_trailing_slashes (link);
1546       parent = g_path_get_dirname (link2);
1547       g_free (link2);
1548       
1549       resolved = g_build_filename (parent, symlink_value, NULL);
1550       g_free (parent);
1551       
1552       canonical = canonicalize_filename (resolved);
1553       
1554       g_free (resolved);
1555
1556       return canonical;
1557     }
1558 }
1559
1560 static char *
1561 get_parent (const char *path, 
1562             dev_t      *parent_dev)
1563 {
1564   char *parent, *tmp;
1565   GStatBuf parent_stat;
1566   int num_recursions;
1567   char *path_copy;
1568
1569   path_copy = strip_trailing_slashes (path);
1570   
1571   parent = g_path_get_dirname (path_copy);
1572   if (strcmp (parent, ".") == 0 ||
1573       strcmp (parent, path_copy) == 0)
1574     {
1575       g_free (parent);
1576       g_free (path_copy);
1577       return NULL;
1578     }
1579   g_free (path_copy);
1580
1581   num_recursions = 0;
1582   do {
1583     if (g_lstat (parent, &parent_stat) != 0)
1584       {
1585         g_free (parent);
1586         return NULL;
1587       }
1588     
1589     if (S_ISLNK (parent_stat.st_mode))
1590       {
1591         tmp = parent;
1592         parent = expand_symlink (parent);
1593         g_free (tmp);
1594       }
1595     
1596     num_recursions++;
1597     if (num_recursions > 12)
1598       {
1599         g_free (parent);
1600         return NULL;
1601       }
1602   } while (S_ISLNK (parent_stat.st_mode));
1603
1604   *parent_dev = parent_stat.st_dev;
1605   
1606   return parent;
1607 }
1608
1609 static char *
1610 expand_all_symlinks (const char *path)
1611 {
1612   char *parent, *parent_expanded;
1613   char *basename, *res;
1614   dev_t parent_dev;
1615
1616   parent = get_parent (path, &parent_dev);
1617   if (parent)
1618     {
1619       parent_expanded = expand_all_symlinks (parent);
1620       g_free (parent);
1621       basename = g_path_get_basename (path);
1622       res = g_build_filename (parent_expanded, basename, NULL);
1623       g_free (basename);
1624       g_free (parent_expanded);
1625     }
1626   else
1627     res = g_strdup (path);
1628   
1629   return res;
1630 }
1631
1632 static char *
1633 find_mountpoint_for (const char *file, 
1634                      dev_t       dev)
1635 {
1636   char *dir, *parent;
1637   dev_t dir_dev, parent_dev;
1638
1639   dir = g_strdup (file);
1640   dir_dev = dev;
1641
1642   while (1) 
1643     {
1644       parent = get_parent (dir, &parent_dev);
1645       if (parent == NULL)
1646         return dir;
1647     
1648       if (parent_dev != dir_dev)
1649         {
1650           g_free (parent);
1651           return dir;
1652         }
1653     
1654       g_free (dir);
1655       dir = parent;
1656     }
1657 }
1658
1659 static char *
1660 find_topdir_for (const char *file)
1661 {
1662   char *dir;
1663   dev_t dir_dev;
1664
1665   dir = get_parent (file, &dir_dev);
1666   if (dir == NULL)
1667     return NULL;
1668
1669   return find_mountpoint_for (dir, dir_dev);
1670 }
1671
1672 static char *
1673 get_unique_filename (const char *basename, 
1674                      int         id)
1675 {
1676   const char *dot;
1677       
1678   if (id == 1)
1679     return g_strdup (basename);
1680
1681   dot = strchr (basename, '.');
1682   if (dot)
1683     return g_strdup_printf ("%.*s.%d%s", (int)(dot - basename), basename, id, dot);
1684   else
1685     return g_strdup_printf ("%s.%d", basename, id);
1686 }
1687
1688 static gboolean
1689 path_has_prefix (const char *path, 
1690                  const char *prefix)
1691 {
1692   int prefix_len;
1693
1694   if (prefix == NULL)
1695     return TRUE;
1696
1697   prefix_len = strlen (prefix);
1698   
1699   if (strncmp (path, prefix, prefix_len) == 0 &&
1700       (prefix_len == 0 || /* empty prefix always matches */
1701        prefix[prefix_len - 1] == '/' || /* last char in prefix was a /, so it must be in path too */
1702        path[prefix_len] == 0 ||
1703        path[prefix_len] == '/'))
1704     return TRUE;
1705   
1706   return FALSE;
1707 }
1708
1709 static char *
1710 try_make_relative (const char *path, 
1711                    const char *base)
1712 {
1713   char *path2, *base2;
1714   char *relative;
1715
1716   path2 = expand_all_symlinks (path);
1717   base2 = expand_all_symlinks (base);
1718
1719   relative = NULL;
1720   if (path_has_prefix (path2, base2))
1721     {
1722       relative = path2 + strlen (base2);
1723       while (*relative == '/')
1724         relative ++;
1725       relative = g_strdup (relative);
1726     }
1727   g_free (path2);
1728   g_free (base2);
1729
1730   if (relative)
1731     return relative;
1732   
1733   /* Failed, use abs path */
1734   return g_strdup (path);
1735 }
1736
1737 static char *
1738 escape_trash_name (char *name)
1739 {
1740   GString *str;
1741   const gchar hex[16] = "0123456789ABCDEF";
1742   
1743   str = g_string_new ("");
1744
1745   while (*name != 0)
1746     {
1747       char c;
1748
1749       c = *name++;
1750
1751       if (g_ascii_isprint (c))
1752         g_string_append_c (str, c);
1753       else
1754         {
1755           g_string_append_c (str, '%');
1756           g_string_append_c (str, hex[((guchar)c) >> 4]);
1757           g_string_append_c (str, hex[((guchar)c) & 0xf]);
1758         }
1759     }
1760
1761   return g_string_free (str, FALSE);
1762 }
1763
1764 gboolean
1765 _g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev)
1766 {
1767   static gsize home_dev_set = 0;
1768   static dev_t home_dev;
1769   char *topdir, *globaldir, *trashdir, *tmpname;
1770   uid_t uid;
1771   char uid_str[32];
1772   GStatBuf global_stat, trash_stat;
1773   gboolean res;
1774
1775   if (g_once_init_enter (&home_dev_set))
1776     {
1777       GStatBuf home_stat;
1778
1779       g_stat (g_get_home_dir (), &home_stat);
1780       home_dev = home_stat.st_dev;
1781       g_once_init_leave (&home_dev_set, 1);
1782     }
1783
1784   /* Assume we can trash to the home */
1785   if (dir_dev == home_dev)
1786     return TRUE;
1787
1788   topdir = find_mountpoint_for (dirname, dir_dev);
1789   if (topdir == NULL)
1790     return FALSE;
1791
1792   globaldir = g_build_filename (topdir, ".Trash", NULL);
1793   if (g_lstat (globaldir, &global_stat) == 0 &&
1794       S_ISDIR (global_stat.st_mode) &&
1795       (global_stat.st_mode & S_ISVTX) != 0)
1796     {
1797       /* got a toplevel sysadmin created dir, assume we
1798        * can trash to it (we should be able to create a dir)
1799        * This fails for the FAT case where the ownership of
1800        * that dir would be wrong though..
1801        */
1802       g_free (globaldir);
1803       g_free (topdir);
1804       return TRUE;
1805     }
1806   g_free (globaldir);
1807
1808   /* No global trash dir, or it failed the tests, fall back to $topdir/.Trash-$uid */
1809   uid = geteuid ();
1810   g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long) uid);
1811
1812   tmpname = g_strdup_printf (".Trash-%s", uid_str);
1813   trashdir = g_build_filename (topdir, tmpname, NULL);
1814   g_free (tmpname);
1815
1816   if (g_lstat (trashdir, &trash_stat) == 0)
1817     {
1818       g_free (topdir);
1819       g_free (trashdir);
1820       return S_ISDIR (trash_stat.st_mode) &&
1821              trash_stat.st_uid == uid;
1822     }
1823   g_free (trashdir);
1824
1825   /* User specific trash didn't exist, can we create it? */
1826   res = g_access (topdir, W_OK) == 0;
1827   g_free (topdir);
1828
1829   return res;
1830 }
1831
1832
1833 static gboolean
1834 g_local_file_trash (GFile         *file,
1835                     GCancellable  *cancellable,
1836                     GError       **error)
1837 {
1838   GLocalFile *local = G_LOCAL_FILE (file);
1839   GStatBuf file_stat, home_stat;
1840   const char *homedir;
1841   char *trashdir, *topdir, *infodir, *filesdir;
1842   char *basename, *trashname, *trashfile, *infoname, *infofile;
1843   char *original_name, *original_name_escaped;
1844   int i;
1845   char *data;
1846   gboolean is_homedir_trash;
1847   char delete_time[32];
1848   int fd;
1849   GStatBuf trash_stat, global_stat;
1850   char *dirname, *globaldir;
1851   GVfsClass *class;
1852   GVfs *vfs;
1853
1854   if (g_lstat (local->filename, &file_stat) != 0)
1855     {
1856       int errsv = errno;
1857
1858       g_set_error (error, G_IO_ERROR,
1859                    g_io_error_from_errno (errsv),
1860                    _("Error trashing file: %s"),
1861                    g_strerror (errsv));
1862       return FALSE;
1863     }
1864     
1865   homedir = g_get_home_dir ();
1866   g_stat (homedir, &home_stat);
1867
1868   is_homedir_trash = FALSE;
1869   trashdir = NULL;
1870   if (file_stat.st_dev == home_stat.st_dev)
1871     {
1872       is_homedir_trash = TRUE;
1873       errno = 0;
1874       trashdir = g_build_filename (g_get_user_data_dir (), "Trash", NULL);
1875       if (g_mkdir_with_parents (trashdir, 0700) < 0)
1876         {
1877           char *display_name;
1878           int errsv = errno;
1879
1880           display_name = g_filename_display_name (trashdir);
1881           g_set_error (error, G_IO_ERROR,
1882                        g_io_error_from_errno (errsv),
1883                        _("Unable to create trash dir %s: %s"),
1884                        display_name, g_strerror (errsv));
1885           g_free (display_name);
1886           g_free (trashdir);
1887           return FALSE;
1888         }
1889       topdir = g_strdup (g_get_user_data_dir ());
1890     }
1891   else
1892     {
1893       uid_t uid;
1894       char uid_str[32];
1895
1896       uid = geteuid ();
1897       g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long)uid);
1898       
1899       topdir = find_topdir_for (local->filename);
1900       if (topdir == NULL)
1901         {
1902           g_set_error_literal (error, G_IO_ERROR,
1903                                G_IO_ERROR_NOT_SUPPORTED,
1904                                _("Unable to find toplevel directory for trash"));
1905           return FALSE;
1906         }
1907       
1908       /* Try looking for global trash dir $topdir/.Trash/$uid */
1909       globaldir = g_build_filename (topdir, ".Trash", NULL);
1910       if (g_lstat (globaldir, &global_stat) == 0 &&
1911           S_ISDIR (global_stat.st_mode) &&
1912           (global_stat.st_mode & S_ISVTX) != 0)
1913         {
1914           trashdir = g_build_filename (globaldir, uid_str, NULL);
1915
1916           if (g_lstat (trashdir, &trash_stat) == 0)
1917             {
1918               if (!S_ISDIR (trash_stat.st_mode) ||
1919                   trash_stat.st_uid != uid)
1920                 {
1921                   /* Not a directory or not owned by user, ignore */
1922                   g_free (trashdir);
1923                   trashdir = NULL;
1924                 }
1925             }
1926           else if (g_mkdir (trashdir, 0700) == -1)
1927             {
1928               g_free (trashdir);
1929               trashdir = NULL;
1930             }
1931         }
1932       g_free (globaldir);
1933
1934       if (trashdir == NULL)
1935         {
1936           gboolean tried_create;
1937           
1938           /* No global trash dir, or it failed the tests, fall back to $topdir/.Trash-$uid */
1939           dirname = g_strdup_printf (".Trash-%s", uid_str);
1940           trashdir = g_build_filename (topdir, dirname, NULL);
1941           g_free (dirname);
1942
1943           tried_create = FALSE;
1944
1945         retry:
1946           if (g_lstat (trashdir, &trash_stat) == 0)
1947             {
1948               if (!S_ISDIR (trash_stat.st_mode) ||
1949                   trash_stat.st_uid != uid)
1950                 {
1951                   /* Remove the failed directory */
1952                   if (tried_create)
1953                     g_remove (trashdir);
1954                   
1955                   /* Not a directory or not owned by user, ignore */
1956                   g_free (trashdir);
1957                   trashdir = NULL;
1958                 }
1959             }
1960           else
1961             {
1962               if (!tried_create &&
1963                   g_mkdir (trashdir, 0700) != -1)
1964                 {
1965                   /* Ensure that the created dir has the right uid etc.
1966                      This might fail on e.g. a FAT dir */
1967                   tried_create = TRUE;
1968                   goto retry;
1969                 }
1970               else
1971                 {
1972                   g_free (trashdir);
1973                   trashdir = NULL;
1974                 }
1975             }
1976         }
1977
1978       if (trashdir == NULL)
1979         {
1980           g_free (topdir);
1981           g_set_error_literal (error, G_IO_ERROR,
1982                                G_IO_ERROR_NOT_SUPPORTED,
1983                                _("Unable to find or create trash directory"));
1984           return FALSE;
1985         }
1986     }
1987
1988   /* Trashdir points to the trash dir with the "info" and "files" subdirectories */
1989
1990   infodir = g_build_filename (trashdir, "info", NULL);
1991   filesdir = g_build_filename (trashdir, "files", NULL);
1992   g_free (trashdir);
1993
1994   /* Make sure we have the subdirectories */
1995   if ((g_mkdir (infodir, 0700) == -1 && errno != EEXIST) ||
1996       (g_mkdir (filesdir, 0700) == -1 && errno != EEXIST))
1997     {
1998       g_free (topdir);
1999       g_free (infodir);
2000       g_free (filesdir);
2001       g_set_error_literal (error, G_IO_ERROR,
2002                            G_IO_ERROR_NOT_SUPPORTED,
2003                            _("Unable to find or create trash directory"));
2004       return FALSE;
2005     }  
2006
2007   basename = g_path_get_basename (local->filename);
2008   i = 1;
2009   trashname = NULL;
2010   infofile = NULL;
2011   do {
2012     g_free (trashname);
2013     g_free (infofile);
2014     
2015     trashname = get_unique_filename (basename, i++);
2016     infoname = g_strconcat (trashname, ".trashinfo", NULL);
2017     infofile = g_build_filename (infodir, infoname, NULL);
2018     g_free (infoname);
2019
2020     fd = open (infofile, O_CREAT | O_EXCL, 0666);
2021   } while (fd == -1 && errno == EEXIST);
2022
2023   g_free (basename);
2024   g_free (infodir);
2025
2026   if (fd == -1)
2027     {
2028       int errsv = errno;
2029
2030       g_free (filesdir);
2031       g_free (topdir);
2032       g_free (trashname);
2033       g_free (infofile);
2034       
2035       g_set_error (error, G_IO_ERROR,
2036                    g_io_error_from_errno (errsv),
2037                    _("Unable to create trashing info file: %s"),
2038                    g_strerror (errsv));
2039       return FALSE;
2040     }
2041
2042   close (fd);
2043
2044   /* TODO: Maybe we should verify that you can delete the file from the trash
2045      before moving it? OTOH, that is hard, as it needs a recursive scan */
2046
2047   trashfile = g_build_filename (filesdir, trashname, NULL);
2048
2049   g_free (filesdir);
2050
2051   if (g_rename (local->filename, trashfile) == -1)
2052     {
2053       int errsv = errno;
2054
2055       g_free (topdir);
2056       g_free (trashname);
2057       g_free (infofile);
2058       g_free (trashfile);
2059
2060       if (errsv == EXDEV)
2061         /* The trash dir was actually on another fs anyway!?
2062            This can happen when the same device is mounted multiple
2063            times, or with bind mounts of the same fs. */
2064         g_set_error (error, G_IO_ERROR,
2065                      G_IO_ERROR_NOT_SUPPORTED,
2066                      _("Unable to trash file: %s"),
2067                      g_strerror (errsv));
2068       else
2069         g_set_error (error, G_IO_ERROR,
2070                      g_io_error_from_errno (errsv),
2071                      _("Unable to trash file: %s"),
2072                      g_strerror (errsv));
2073       return FALSE;
2074     }
2075
2076   vfs = g_vfs_get_default ();
2077   class = G_VFS_GET_CLASS (vfs);
2078   if (class->local_file_moved)
2079     class->local_file_moved (vfs, local->filename, trashfile);
2080
2081   g_free (trashfile);
2082
2083   /* TODO: Do we need to update mtime/atime here after the move? */
2084
2085   /* Use absolute names for homedir */
2086   if (is_homedir_trash)
2087     original_name = g_strdup (local->filename);
2088   else
2089     original_name = try_make_relative (local->filename, topdir);
2090   original_name_escaped = escape_trash_name (original_name);
2091   
2092   g_free (original_name);
2093   g_free (topdir);
2094   
2095   {
2096     time_t t;
2097     struct tm now;
2098     t = time (NULL);
2099     localtime_r (&t, &now);
2100     delete_time[0] = 0;
2101     strftime(delete_time, sizeof (delete_time), "%Y-%m-%dT%H:%M:%S", &now);
2102   }
2103
2104   data = g_strdup_printf ("[Trash Info]\nPath=%s\nDeletionDate=%s\n",
2105                           original_name_escaped, delete_time);
2106
2107   g_file_set_contents (infofile, data, -1, NULL);
2108   g_free (infofile);
2109   g_free (data);
2110   
2111   g_free (original_name_escaped);
2112   g_free (trashname);
2113   
2114   return TRUE;
2115 }
2116 #else /* G_OS_WIN32 */
2117 gboolean
2118 _g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev)
2119 {
2120   return FALSE;                 /* XXX ??? */
2121 }
2122
2123 static gboolean
2124 g_local_file_trash (GFile         *file,
2125                     GCancellable  *cancellable,
2126                     GError       **error)
2127 {
2128   GLocalFile *local = G_LOCAL_FILE (file);
2129   SHFILEOPSTRUCTW op = {0};
2130   gboolean success;
2131   wchar_t *wfilename;
2132   long len;
2133
2134   wfilename = g_utf8_to_utf16 (local->filename, -1, NULL, &len, NULL);
2135   /* SHFILEOPSTRUCT.pFrom is double-zero-terminated */
2136   wfilename = g_renew (wchar_t, wfilename, len + 2);
2137   wfilename[len + 1] = 0;
2138
2139   op.wFunc = FO_DELETE;
2140   op.pFrom = wfilename;
2141   op.fFlags = FOF_ALLOWUNDO;
2142
2143   success = SHFileOperationW (&op) == 0;
2144
2145   if (success && op.fAnyOperationsAborted)
2146     {
2147       if (cancellable && !g_cancellable_is_cancelled (cancellable))
2148         g_cancellable_cancel (cancellable);
2149       g_set_error (error, G_IO_ERROR,
2150                    G_IO_ERROR_CANCELLED,
2151                    _("Unable to trash file: %s"),
2152                    _("Operation was cancelled"));
2153       success = FALSE;
2154     }
2155   else if (!success)
2156     g_set_error (error, G_IO_ERROR,
2157                  G_IO_ERROR_FAILED,
2158                  _("Unable to trash file: %s"),
2159                  _("internal error"));
2160
2161   g_free (wfilename);
2162   return success;
2163 }
2164 #endif /* G_OS_WIN32 */
2165
2166 static gboolean
2167 g_local_file_make_directory (GFile         *file,
2168                              GCancellable  *cancellable,
2169                              GError       **error)
2170 {
2171   GLocalFile *local = G_LOCAL_FILE (file);
2172   
2173   if (g_mkdir (local->filename, 0777) == -1)
2174     {
2175       int errsv = errno;
2176
2177       if (errsv == EINVAL)
2178         /* This must be an invalid filename, on e.g. FAT */
2179         g_set_error_literal (error, G_IO_ERROR,
2180                              G_IO_ERROR_INVALID_FILENAME,
2181                              _("Invalid filename"));
2182       else
2183         g_set_error (error, G_IO_ERROR,
2184                      g_io_error_from_errno (errsv),
2185                      _("Error creating directory: %s"),
2186                      g_strerror (errsv));
2187       return FALSE;
2188     }
2189   
2190   return TRUE;
2191 }
2192
2193 static gboolean
2194 g_local_file_make_symbolic_link (GFile         *file,
2195                                  const char    *symlink_value,
2196                                  GCancellable  *cancellable,
2197                                  GError       **error)
2198 {
2199 #ifdef HAVE_SYMLINK
2200   GLocalFile *local = G_LOCAL_FILE (file);
2201   
2202   if (symlink (symlink_value, local->filename) == -1)
2203     {
2204       int errsv = errno;
2205
2206       if (errsv == EINVAL)
2207         /* This must be an invalid filename, on e.g. FAT */
2208         g_set_error_literal (error, G_IO_ERROR,
2209                              G_IO_ERROR_INVALID_FILENAME,
2210                              _("Invalid filename"));
2211       else if (errsv == EPERM)
2212         g_set_error (error, G_IO_ERROR,
2213                      G_IO_ERROR_NOT_SUPPORTED,
2214                      _("Filesystem does not support symbolic links"));
2215       else
2216         g_set_error (error, G_IO_ERROR,
2217                      g_io_error_from_errno (errsv),
2218                      _("Error making symbolic link: %s"),
2219                      g_strerror (errsv));
2220       return FALSE;
2221     }
2222   return TRUE;
2223 #else
2224   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Symlinks not supported");
2225   return FALSE;
2226 #endif
2227 }
2228
2229
2230 static gboolean
2231 g_local_file_copy (GFile                  *source,
2232                    GFile                  *destination,
2233                    GFileCopyFlags          flags,
2234                    GCancellable           *cancellable,
2235                    GFileProgressCallback   progress_callback,
2236                    gpointer                progress_callback_data,
2237                    GError                **error)
2238 {
2239   /* Fall back to default copy */
2240   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Copy not supported");
2241   return FALSE;
2242 }
2243
2244 static gboolean
2245 g_local_file_move (GFile                  *source,
2246                    GFile                  *destination,
2247                    GFileCopyFlags          flags,
2248                    GCancellable           *cancellable,
2249                    GFileProgressCallback   progress_callback,
2250                    gpointer                progress_callback_data,
2251                    GError                **error)
2252 {
2253   GLocalFile *local_source, *local_destination;
2254   GStatBuf statbuf;
2255   gboolean destination_exist, source_is_dir;
2256   char *backup_name;
2257   int res;
2258   off_t source_size;
2259   GVfsClass *class;
2260   GVfs *vfs;
2261
2262   if (!G_IS_LOCAL_FILE (source) ||
2263       !G_IS_LOCAL_FILE (destination))
2264     {
2265       /* Fall back to default move */
2266       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Move not supported");
2267       return FALSE;
2268     }
2269   
2270   local_source = G_LOCAL_FILE (source);
2271   local_destination = G_LOCAL_FILE (destination);
2272   
2273   res = g_lstat (local_source->filename, &statbuf);
2274   if (res == -1)
2275     {
2276       int errsv = errno;
2277
2278       g_set_error (error, G_IO_ERROR,
2279                    g_io_error_from_errno (errsv),
2280                    _("Error moving file: %s"),
2281                    g_strerror (errsv));
2282       return FALSE;
2283     }
2284
2285   source_is_dir = S_ISDIR (statbuf.st_mode);
2286   source_size = statbuf.st_size;
2287   
2288   destination_exist = FALSE;
2289   res = g_lstat (local_destination->filename, &statbuf);
2290   if (res == 0)
2291     {
2292       destination_exist = TRUE; /* Target file exists */
2293
2294       if (flags & G_FILE_COPY_OVERWRITE)
2295         {
2296           /* Always fail on dirs, even with overwrite */
2297           if (S_ISDIR (statbuf.st_mode))
2298             {
2299               if (source_is_dir)
2300                 g_set_error_literal (error,
2301                                      G_IO_ERROR,
2302                                      G_IO_ERROR_WOULD_MERGE,
2303                                      _("Can't move directory over directory"));
2304               else
2305                 g_set_error_literal (error,
2306                                      G_IO_ERROR,
2307                                      G_IO_ERROR_IS_DIRECTORY,
2308                                      _("Can't copy over directory"));
2309               return FALSE;
2310             }
2311         }
2312       else
2313         {
2314           g_set_error_literal (error,
2315                                G_IO_ERROR,
2316                                G_IO_ERROR_EXISTS,
2317                                _("Target file exists"));
2318           return FALSE;
2319         }
2320     }
2321   
2322   if (flags & G_FILE_COPY_BACKUP && destination_exist)
2323     {
2324       backup_name = g_strconcat (local_destination->filename, "~", NULL);
2325       if (g_rename (local_destination->filename, backup_name) == -1)
2326         {
2327           g_set_error_literal (error,
2328                                G_IO_ERROR,
2329                                G_IO_ERROR_CANT_CREATE_BACKUP,
2330                                _("Backup file creation failed"));
2331           g_free (backup_name);
2332           return FALSE;
2333         }
2334       g_free (backup_name);
2335       destination_exist = FALSE; /* It did, but no more */
2336     }
2337
2338   if (source_is_dir && destination_exist && (flags & G_FILE_COPY_OVERWRITE))
2339     {
2340       /* Source is a dir, destination exists (and is not a dir, because that would have failed
2341          earlier), and we're overwriting. Manually remove the target so we can do the rename. */
2342       res = g_unlink (local_destination->filename);
2343       if (res == -1)
2344         {
2345           int errsv = errno;
2346
2347           g_set_error (error, G_IO_ERROR,
2348                        g_io_error_from_errno (errsv),
2349                        _("Error removing target file: %s"),
2350                        g_strerror (errsv));
2351           return FALSE;
2352         }
2353     }
2354   
2355   if (g_rename (local_source->filename, local_destination->filename) == -1)
2356     {
2357       int errsv = errno;
2358
2359       if (errsv == EXDEV)
2360         /* This will cause the fallback code to run */
2361         g_set_error_literal (error, G_IO_ERROR,
2362                              G_IO_ERROR_NOT_SUPPORTED,
2363                              _("Move between mounts not supported"));
2364       else if (errsv == EINVAL)
2365         /* This must be an invalid filename, on e.g. FAT, or
2366            we're trying to move the file into itself...
2367            We return invalid filename for both... */
2368         g_set_error_literal (error, G_IO_ERROR,
2369                              G_IO_ERROR_INVALID_FILENAME,
2370                              _("Invalid filename"));
2371       else
2372         g_set_error (error, G_IO_ERROR,
2373                      g_io_error_from_errno (errsv),
2374                      _("Error moving file: %s"),
2375                      g_strerror (errsv));
2376       return FALSE;
2377     }
2378
2379   vfs = g_vfs_get_default ();
2380   class = G_VFS_GET_CLASS (vfs);
2381   if (class->local_file_moved)
2382     class->local_file_moved (vfs, local_source->filename, local_destination->filename);
2383
2384   /* Make sure we send full copied size */
2385   if (progress_callback)
2386     progress_callback (source_size, source_size, progress_callback_data);
2387   
2388   return TRUE;
2389 }
2390
2391 static GFileMonitor*
2392 g_local_file_monitor_dir (GFile             *file,
2393                           GFileMonitorFlags  flags,
2394                           GCancellable      *cancellable,
2395                           GError           **error)
2396 {
2397   GLocalFile* local_file = G_LOCAL_FILE(file);
2398   return _g_local_directory_monitor_new (local_file->filename, flags, error);
2399 }
2400
2401 static GFileMonitor*
2402 g_local_file_monitor_file (GFile             *file,
2403                            GFileMonitorFlags  flags,
2404                            GCancellable      *cancellable,
2405                            GError           **error)
2406 {
2407   GLocalFile* local_file = G_LOCAL_FILE(file);
2408   return _g_local_file_monitor_new (local_file->filename, flags, error);
2409 }
2410
2411 static void
2412 g_local_file_file_iface_init (GFileIface *iface)
2413 {
2414   iface->dup = g_local_file_dup;
2415   iface->hash = g_local_file_hash;
2416   iface->equal = g_local_file_equal;
2417   iface->is_native = g_local_file_is_native;
2418   iface->has_uri_scheme = g_local_file_has_uri_scheme;
2419   iface->get_uri_scheme = g_local_file_get_uri_scheme;
2420   iface->get_basename = g_local_file_get_basename;
2421   iface->get_path = g_local_file_get_path;
2422   iface->get_uri = g_local_file_get_uri;
2423   iface->get_parse_name = g_local_file_get_parse_name;
2424   iface->get_parent = g_local_file_get_parent;
2425   iface->prefix_matches = g_local_file_prefix_matches;
2426   iface->get_relative_path = g_local_file_get_relative_path;
2427   iface->resolve_relative_path = g_local_file_resolve_relative_path;
2428   iface->get_child_for_display_name = g_local_file_get_child_for_display_name;
2429   iface->set_display_name = g_local_file_set_display_name;
2430   iface->enumerate_children = g_local_file_enumerate_children;
2431   iface->query_info = g_local_file_query_info;
2432   iface->query_filesystem_info = g_local_file_query_filesystem_info;
2433   iface->find_enclosing_mount = g_local_file_find_enclosing_mount;
2434   iface->query_settable_attributes = g_local_file_query_settable_attributes;
2435   iface->query_writable_namespaces = g_local_file_query_writable_namespaces;
2436   iface->set_attribute = g_local_file_set_attribute;
2437   iface->set_attributes_from_info = g_local_file_set_attributes_from_info;
2438   iface->read_fn = g_local_file_read;
2439   iface->append_to = g_local_file_append_to;
2440   iface->create = g_local_file_create;
2441   iface->replace = g_local_file_replace;
2442   iface->open_readwrite = g_local_file_open_readwrite;
2443   iface->create_readwrite = g_local_file_create_readwrite;
2444   iface->replace_readwrite = g_local_file_replace_readwrite;
2445   iface->delete_file = g_local_file_delete;
2446   iface->trash = g_local_file_trash;
2447   iface->make_directory = g_local_file_make_directory;
2448   iface->make_symbolic_link = g_local_file_make_symbolic_link;
2449   iface->copy = g_local_file_copy;
2450   iface->move = g_local_file_move;
2451   iface->monitor_dir = g_local_file_monitor_dir;
2452   iface->monitor_file = g_local_file_monitor_file;
2453
2454   iface->supports_thread_contexts = TRUE;
2455 }