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