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