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