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