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