1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
/* file-method.c - Local file access method for the GNOME Virtual File
5
Copyright (C) 1999 Free Software Foundation
7
The Gnome Library is free software; you can redistribute it and/or
8
modify it under the terms of the GNU Library General Public License as
9
published by the Free Software Foundation; either version 2 of the
10
License, or (at your option) any later version.
12
The Gnome Library is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
Library General Public License for more details.
17
You should have received a copy of the GNU Library General Public
18
License along with the Gnome Library; see the file COPYING.LIB. If not,
19
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
Boston, MA 02111-1307, USA.
23
Ettore Perazzoli <ettore@comm2000.it>
24
Pavel Cisler <pavel@eazel.com>
29
#include <libgnomevfs/gnome-vfs-cancellation.h>
30
#include <libgnomevfs/gnome-vfs-context.h>
31
#include <libgnomevfs/gnome-vfs-method.h>
32
#include <libgnomevfs/gnome-vfs-mime.h>
33
#include <libgnomevfs/gnome-vfs-module-shared.h>
34
#include <libgnomevfs/gnome-vfs-module.h>
35
#include <libgnomevfs/gnome-vfs-utils.h>
36
#include <libgnomevfs/gnome-vfs-mime.h>
37
#include <libgnomevfs/gnome-vfs-monitor-private.h>
38
#include <libgnomevfs/gnome-vfs-private-utils.h>
39
#include <libgnomevfs/gnome-vfs-ops.h>
41
#include <glib/gi18n-lib.h>
42
#include <glib/gstdio.h>
53
#include <sys/types.h>
54
#ifndef G_OS_WIN32 /* We don't want the ftruncate() in mingw's unistd.h */
64
#include <selinux/selinux.h>
67
#if defined(HAVE_LINUX_INOTIFY_H) || defined(HAVE_SYS_INOTIFY_H)
69
#include "inotify-helper.h"
72
#if HAVE_SYS_STATVFS_H
73
#include <sys/statvfs.h>
77
#elif HAVE_SYS_MOUNT_H
79
#include <sys/param.h>
81
#include <sys/mount.h>
84
#include "file-method-acl.h"
91
#define DIR_SEPARATORS "/\\"
93
#define DIR_SEPARATORS "/"
97
GnomeVFSMethodMonitorCancelFunc cancel_func; /* Must be first */
98
} AnyFileMonitorHandle;
101
static FAMConnection *fam_connection = NULL;
102
static gint fam_watch_id = 0;
103
G_LOCK_DEFINE_STATIC (fam_connection);
106
GnomeVFSMethodMonitorCancelFunc cancel_func; /* Must be first */
114
#define GET_PATH_MAX() PATH_MAX
119
static unsigned int value;
121
/* This code is copied from GNU make. It returns the maximum
122
path length by using `pathconf'. */
125
long int x = pathconf(G_DIR_SEPARATOR_S, _PC_PATH_MAX);
147
#if defined(HAVE_LSEEK64) && defined(HAVE_OFF64_T)
148
#define LSEEK lseek64
149
#define OFF_T off64_t
161
/* FIXME: not threadsafe at all! */
162
LARGE_INTEGER origpos;
165
origpos.QuadPart = 0;
166
origpos.u.LowPart = SetFilePointer ((HANDLE) _get_osfhandle (fd), 0,
167
&origpos.u.HighPart, FILE_CURRENT);
168
if (origpos.u.LowPart != INVALID_SET_FILE_POINTER) {
169
LARGE_INTEGER newpos;
171
newpos.QuadPart = size;
172
if (SetFilePointer ((HANDLE) _get_osfhandle (fd), newpos.u.LowPart,
173
&newpos.u.HighPart, FILE_BEGIN) != INVALID_SET_FILE_POINTER &&
174
SetEndOfFile ((HANDLE) _get_osfhandle (fd)) &&
175
SetFilePointer ((HANDLE) _get_osfhandle (fd), origpos.u.LowPart,
176
&origpos.u.HighPart, FILE_BEGIN) != INVALID_SET_FILE_POINTER)
189
get_path_from_uri (GnomeVFSURI const *uri)
193
path = gnome_vfs_unescape_string (uri->text, DIR_SEPARATORS);
199
if (!g_path_is_absolute (path)) {
204
/* Drop slash in front of drive letter */
205
if (path[0] == '/' && g_ascii_isalpha (path[1]) && path[2] == ':') {
207
path = g_strdup (path + 1);
215
get_base_from_uri (GnomeVFSURI const *uri)
217
gchar *escaped_base, *base;
219
escaped_base = gnome_vfs_uri_extract_short_path_name (uri);
220
base = gnome_vfs_unescape_string (escaped_base, DIR_SEPARATORS);
221
g_free (escaped_base);
231
file_handle_new (GnomeVFSURI *uri,
235
result = g_new (FileHandle, 1);
237
result->uri = gnome_vfs_uri_ref (uri);
244
file_handle_destroy (FileHandle *handle)
246
gnome_vfs_uri_unref (handle->uri);
250
static GnomeVFSResult
251
do_open (GnomeVFSMethod *method,
252
GnomeVFSMethodHandle **method_handle,
254
GnomeVFSOpenMode mode,
255
GnomeVFSContext *context)
257
FileHandle *file_handle;
263
_GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
264
_GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
266
if (mode & GNOME_VFS_OPEN_READ) {
267
if (mode & GNOME_VFS_OPEN_WRITE)
270
unix_mode = O_RDONLY;
272
if (mode & GNOME_VFS_OPEN_WRITE)
273
unix_mode = O_WRONLY;
275
return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
278
unix_mode |= _O_BINARY;
281
if ((mode & GNOME_VFS_OPEN_TRUNCATE) ||
282
(!(mode & GNOME_VFS_OPEN_RANDOM) && (mode & GNOME_VFS_OPEN_WRITE)))
283
unix_mode |= O_TRUNC;
285
file_name = get_path_from_uri (uri);
286
if (file_name == NULL)
287
return GNOME_VFS_ERROR_INVALID_URI;
290
fd = OPEN (file_name, unix_mode, 0);
293
&& ! gnome_vfs_context_check_cancellation (context));
298
return gnome_vfs_result_from_errno ();
300
#ifdef HAVE_POSIX_FADVISE
301
if (! (mode & GNOME_VFS_OPEN_RANDOM)) {
302
posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
306
if (fstat (fd, &statbuf) != 0)
307
return gnome_vfs_result_from_errno ();
309
if (S_ISDIR (statbuf.st_mode)) {
311
return GNOME_VFS_ERROR_IS_DIRECTORY;
314
file_handle = file_handle_new (uri, fd);
316
*method_handle = (GnomeVFSMethodHandle *) file_handle;
321
static GnomeVFSResult
322
do_create (GnomeVFSMethod *method,
323
GnomeVFSMethodHandle **method_handle,
325
GnomeVFSOpenMode mode,
328
GnomeVFSContext *context)
330
FileHandle *file_handle;
335
_GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
336
_GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
338
unix_mode = O_CREAT | O_TRUNC;
341
unix_mode |= _O_BINARY;
343
if (!(mode & GNOME_VFS_OPEN_WRITE))
344
return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
346
if (mode & GNOME_VFS_OPEN_READ)
349
unix_mode |= O_WRONLY;
354
file_name = get_path_from_uri (uri);
355
if (file_name == NULL)
356
return GNOME_VFS_ERROR_INVALID_URI;
359
fd = OPEN (file_name, unix_mode, perm);
362
&& ! gnome_vfs_context_check_cancellation (context));
367
return gnome_vfs_result_from_errno ();
369
file_handle = file_handle_new (uri, fd);
371
*method_handle = (GnomeVFSMethodHandle *) file_handle;
376
static GnomeVFSResult
377
do_close (GnomeVFSMethod *method,
378
GnomeVFSMethodHandle *method_handle,
379
GnomeVFSContext *context)
381
FileHandle *file_handle;
384
g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
386
file_handle = (FileHandle *) method_handle;
389
close_retval = close (file_handle->fd);
390
while (close_retval != 0
392
&& ! gnome_vfs_context_check_cancellation (context));
394
/* FIXME bugzilla.eazel.com 1163: Should do this even after a failure? */
395
file_handle_destroy (file_handle);
397
if (close_retval != 0) {
398
return gnome_vfs_result_from_errno ();
404
static GnomeVFSResult
405
do_forget_cache (GnomeVFSMethod *method,
406
GnomeVFSMethodHandle *method_handle,
407
GnomeVFSFileOffset offset,
408
GnomeVFSFileSize size)
410
FileHandle *file_handle;
412
g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
414
file_handle = (FileHandle *) method_handle;
416
#ifdef HAVE_POSIX_FADVISE
417
posix_fadvise (file_handle->fd, offset, size, POSIX_FADV_DONTNEED);
424
static GnomeVFSResult
425
do_read (GnomeVFSMethod *method,
426
GnomeVFSMethodHandle *method_handle,
428
GnomeVFSFileSize num_bytes,
429
GnomeVFSFileSize *bytes_read,
430
GnomeVFSContext *context)
432
FileHandle *file_handle;
435
g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
437
file_handle = (FileHandle *) method_handle;
440
read_val = read (file_handle->fd, buffer, num_bytes);
441
} while (read_val == -1
443
&& ! gnome_vfs_context_check_cancellation (context));
445
if (read_val == -1) {
447
return gnome_vfs_result_from_errno ();
449
*bytes_read = read_val;
451
/* Getting 0 from read() means EOF! */
453
return GNOME_VFS_ERROR_EOF;
459
static GnomeVFSResult
460
do_write (GnomeVFSMethod *method,
461
GnomeVFSMethodHandle *method_handle,
462
gconstpointer buffer,
463
GnomeVFSFileSize num_bytes,
464
GnomeVFSFileSize *bytes_written,
465
GnomeVFSContext *context)
467
FileHandle *file_handle;
470
g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
472
file_handle = (FileHandle *) method_handle;
475
write_val = write (file_handle->fd, buffer, num_bytes);
476
while (write_val == -1
478
&& ! gnome_vfs_context_check_cancellation (context));
480
if (write_val == -1) {
482
return gnome_vfs_result_from_errno ();
484
*bytes_written = write_val;
491
seek_position_to_unix (GnomeVFSSeekPosition position)
494
case GNOME_VFS_SEEK_START:
496
case GNOME_VFS_SEEK_CURRENT:
498
case GNOME_VFS_SEEK_END:
501
g_warning (_("Unknown GnomeVFSSeekPosition %d"), position);
502
return SEEK_SET; /* bogus */
506
static GnomeVFSResult
507
do_seek (GnomeVFSMethod *method,
508
GnomeVFSMethodHandle *method_handle,
509
GnomeVFSSeekPosition whence,
510
GnomeVFSFileOffset offset,
511
GnomeVFSContext *context)
513
FileHandle *file_handle;
516
file_handle = (FileHandle *) method_handle;
517
lseek_whence = seek_position_to_unix (whence);
519
if (LSEEK (file_handle->fd, offset, lseek_whence) == -1) {
521
return GNOME_VFS_ERROR_NOT_SUPPORTED;
523
return gnome_vfs_result_from_errno ();
529
static GnomeVFSResult
530
do_tell (GnomeVFSMethod *method,
531
GnomeVFSMethodHandle *method_handle,
532
GnomeVFSFileSize *offset_return)
534
FileHandle *file_handle;
537
file_handle = (FileHandle *) method_handle;
539
offset = LSEEK (file_handle->fd, 0, SEEK_CUR);
542
return GNOME_VFS_ERROR_NOT_SUPPORTED;
544
return gnome_vfs_result_from_errno ();
547
*offset_return = offset;
552
static GnomeVFSResult
553
do_truncate_handle (GnomeVFSMethod *method,
554
GnomeVFSMethodHandle *method_handle,
555
GnomeVFSFileSize where,
556
GnomeVFSContext *context)
558
FileHandle *file_handle;
560
g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
562
file_handle = (FileHandle *) method_handle;
564
if (ftruncate (file_handle->fd, where) == 0) {
570
return GNOME_VFS_ERROR_READ_ONLY;
572
return GNOME_VFS_ERROR_NOT_SUPPORTED;
574
return GNOME_VFS_ERROR_GENERIC;
579
static GnomeVFSResult
580
do_truncate (GnomeVFSMethod *method,
582
GnomeVFSFileSize where,
583
GnomeVFSContext *context)
588
path = get_path_from_uri (uri);
590
return GNOME_VFS_ERROR_INVALID_URI;
592
if (truncate (path, where) == 0) {
600
return GNOME_VFS_ERROR_READ_ONLY;
602
return GNOME_VFS_ERROR_NOT_SUPPORTED;
604
return GNOME_VFS_ERROR_GENERIC;
608
g_warning ("Not implemented: file::do_truncate()");
609
return GNOME_VFS_ERROR_NOT_SUPPORTED;
615
GnomeVFSFileInfoOptions options;
618
struct dirent *current_entry;
626
static DirectoryHandle *
627
directory_handle_new (GnomeVFSURI *uri,
633
GnomeVFSFileInfoOptions options)
635
DirectoryHandle *result;
639
result = g_new (DirectoryHandle, 1);
641
result->uri = gnome_vfs_uri_ref (uri);
645
/* Reserve extra space for readdir_r, see man page */
646
result->current_entry = g_malloc (sizeof (struct dirent) + GET_PATH_MAX() + 1);
649
full_name = get_path_from_uri (uri);
650
g_assert (full_name != NULL); /* already done by caller */
651
full_name_len = strlen (full_name);
653
result->name_buffer = g_malloc (full_name_len + GET_PATH_MAX () + 2);
654
memcpy (result->name_buffer, full_name, full_name_len);
656
if (full_name_len > 0 && !G_IS_DIR_SEPARATOR (full_name[full_name_len - 1]))
657
result->name_buffer[full_name_len++] = G_DIR_SEPARATOR;
659
result->name_ptr = result->name_buffer + full_name_len;
663
result->options = options;
669
directory_handle_destroy (DirectoryHandle *directory_handle)
671
gnome_vfs_uri_unref (directory_handle->uri);
672
g_free (directory_handle->name_buffer);
674
g_free (directory_handle->current_entry);
676
g_free (directory_handle);
679
/* MIME detection code. */
681
get_mime_type (GnomeVFSFileInfo *info,
682
const char *full_name,
683
GnomeVFSFileInfoOptions options,
684
struct stat *stat_buffer)
686
const char *mime_type;
689
if ((options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS) == 0
690
&& (info->type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK)) {
691
/* we are a symlink and aren't asked to follow -
692
* return the type for a symlink
694
mime_type = "x-special/symlink";
696
if (options & GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE) {
697
mime_type = gnome_vfs_get_file_mime_type (full_name,
699
} else if (options & GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) {
700
mime_type = gnome_vfs_get_file_mime_type (full_name,
703
mime_type = gnome_vfs_get_file_mime_type_fast (full_name,
708
g_assert (mime_type);
709
info->mime_type = g_strdup (mime_type);
710
info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
715
read_link (const gchar *full_name)
721
buffer = g_malloc (size);
726
read_size = readlink (full_name, buffer, size);
731
if (read_size < size) {
732
buffer[read_size] = 0;
736
buffer = g_realloc (buffer, size);
742
/* convert a SELinux scurity context string to a g_malloc() compatible string */
743
static char *sec_con2g_str(char *tmp)
756
/* Get the SELinux security context */
758
get_selinux_context (
759
GnomeVFSFileInfo *info,
760
const char *full_name,
761
GnomeVFSFileInfoOptions options)
764
if (is_selinux_enabled()) {
766
if ((options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS) == 0
767
&& (info->type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK)) {
769
/* we are a symlink and aren't asked to follow -
770
* return the type for a symlink */
772
if (lgetfilecon_raw(full_name, &info->selinux_context) < 0)
773
return gnome_vfs_result_from_errno ();
776
if (getfilecon_raw(full_name, &info->selinux_context) < 0)
777
return gnome_vfs_result_from_errno ();
780
info->selinux_context = sec_con2g_str(info->selinux_context);
782
info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SELINUX_CONTEXT;
789
/* Get the SELinux security context from handle */
791
get_selinux_context_from_handle (
792
GnomeVFSFileInfo *info,
796
if (is_selinux_enabled()) {
797
if (fgetfilecon_raw(handle->fd, &info->selinux_context) >= 0)
798
return gnome_vfs_result_from_errno ();
800
info->selinux_context = sec_con2g_str(info->selinux_context);
802
info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SELINUX_CONTEXT;
809
/* Set the SELinux security context */
811
set_selinux_context (
812
const GnomeVFSFileInfo *info,
813
const char *full_name)
816
if (is_selinux_enabled()) {
817
if (setfilecon_raw(full_name, info->selinux_context) < 0)
818
return gnome_vfs_result_from_errno ();
827
get_access_info (GnomeVFSFileInfo *file_info,
828
const gchar *full_name)
830
/* FIXME: should check errno after calling access because we don't
831
* want to set valid_fields if something bad happened during one
832
* of the access calls
835
if (g_access (full_name, R_OK) == 0) {
836
file_info->permissions |= GNOME_VFS_PERM_ACCESS_READABLE;
838
if (g_access (full_name, W_OK) == 0) {
839
file_info->permissions |= GNOME_VFS_PERM_ACCESS_WRITABLE;
841
if (g_file_test (full_name, G_FILE_TEST_IS_EXECUTABLE)) {
842
file_info->permissions |= GNOME_VFS_PERM_ACCESS_EXECUTABLE;
845
/* Try to minimize the nr of access calls. We rely on read almost
846
* always being allowed in normal cases to keep down the number of
849
if (g_access (full_name, R_OK|W_OK) == 0) {
850
file_info->permissions |= GNOME_VFS_PERM_ACCESS_READABLE | GNOME_VFS_PERM_ACCESS_WRITABLE;
851
if (g_access (full_name, X_OK) == 0) {
852
file_info->permissions |= GNOME_VFS_PERM_ACCESS_EXECUTABLE;
854
} else if (g_access (full_name, R_OK|X_OK) == 0) {
855
file_info->permissions |= GNOME_VFS_PERM_ACCESS_READABLE | GNOME_VFS_PERM_ACCESS_EXECUTABLE;
857
if (g_access (full_name, R_OK) == 0) {
858
file_info->permissions |= GNOME_VFS_PERM_ACCESS_READABLE;
860
if (g_access (full_name, W_OK) == 0) {
861
file_info->permissions |= GNOME_VFS_PERM_ACCESS_WRITABLE;
863
if (g_access (full_name, X_OK) == 0) {
864
file_info->permissions |= GNOME_VFS_PERM_ACCESS_EXECUTABLE;
870
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_ACCESS;
873
static GnomeVFSResult
874
get_stat_info (GnomeVFSFileInfo *file_info,
875
const gchar *full_name,
876
GnomeVFSFileInfoOptions options,
877
struct stat *statptr)
882
char *link_file_path;
891
GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
893
if (statptr == NULL) {
897
if (g_lstat (full_name, statptr) != 0) {
898
return gnome_vfs_result_from_errno ();
903
is_symlink = S_ISLNK (statptr->st_mode);
905
if ((options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS) && is_symlink) {
906
if (g_stat (full_name, statptr) != 0) {
907
if (errno == ELOOP) {
911
/* It's a broken symlink, revert to the lstat. This is sub-optimal but
912
* acceptable because it's not a common case.
914
if (g_lstat (full_name, statptr) != 0) {
915
return gnome_vfs_result_from_errno ();
918
GNOME_VFS_FILE_INFO_SET_SYMLINK (file_info, TRUE);
921
gnome_vfs_stat_to_file_info (file_info, statptr);
926
link_file_path = g_strdup (full_name);
928
/* We will either successfully read the link name or return
929
* NULL if read_link fails -- flag it as a valid field either
932
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SYMLINK_NAME;
935
/* Deal with multiple-level symlinks by following them as
939
g_free (symlink_name);
940
symlink_name = read_link (link_file_path);
941
if (symlink_name == NULL) {
942
g_free (link_file_path);
943
return gnome_vfs_result_from_errno ();
945
if ((options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS) &&
946
symlink_name[0] != '/') {
947
symlink_dir = g_path_get_dirname (link_file_path);
948
newpath = g_build_filename (symlink_dir,
950
g_free (symlink_dir);
951
g_free (symlink_name);
952
symlink_name = gnome_vfs_make_path_name_canonical (newpath);
956
if ((options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS) == 0
957
/* if we had an earlier ELOOP, don't get in an infinite loop here */
959
/* we don't care to follow links */
960
|| g_lstat (symlink_name, statptr) != 0
961
/* we can't make out where this points to */
962
|| !S_ISLNK (statptr->st_mode)) {
963
/* the next level is not a link */
966
g_free (link_file_path);
967
link_file_path = g_strdup (symlink_name);
969
g_free (link_file_path);
971
file_info->symlink_name = symlink_name;
977
static GnomeVFSResult
978
get_stat_info_from_handle (GnomeVFSFileInfo *file_info,
980
GnomeVFSFileInfoOptions options,
981
struct stat *statptr)
985
if (statptr == NULL) {
989
if (fstat (handle->fd, statptr) != 0) {
990
return gnome_vfs_result_from_errno ();
993
gnome_vfs_stat_to_file_info (file_info, statptr);
994
GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
1000
static GnomeVFSResult
1001
do_open_directory (GnomeVFSMethod *method,
1002
GnomeVFSMethodHandle **method_handle,
1004
GnomeVFSFileInfoOptions options,
1005
GnomeVFSContext *context)
1007
gchar *directory_name;
1014
directory_name = get_path_from_uri (uri);
1015
if (directory_name == NULL)
1016
return GNOME_VFS_ERROR_INVALID_URI;
1019
dir = opendir (directory_name);
1021
dir = g_dir_open (directory_name, 0, NULL);
1023
g_free (directory_name);
1025
return gnome_vfs_result_from_errno ();
1028
= (GnomeVFSMethodHandle *) directory_handle_new (uri, dir,
1031
return GNOME_VFS_OK;
1034
static GnomeVFSResult
1035
do_close_directory (GnomeVFSMethod *method,
1036
GnomeVFSMethodHandle *method_handle,
1037
GnomeVFSContext *context)
1039
DirectoryHandle *directory_handle;
1041
directory_handle = (DirectoryHandle *) method_handle;
1044
closedir (directory_handle->dir);
1046
g_dir_close (directory_handle->dir);
1049
directory_handle_destroy (directory_handle);
1051
return GNOME_VFS_OK;
1054
#ifndef HAVE_READDIR_R
1055
G_LOCK_DEFINE_STATIC (readdir);
1058
static GnomeVFSResult
1059
do_read_directory (GnomeVFSMethod *method,
1060
GnomeVFSMethodHandle *method_handle,
1061
GnomeVFSFileInfo *file_info,
1062
GnomeVFSContext *context)
1065
struct dirent *result;
1067
const gchar *result;
1069
struct stat statbuf;
1071
DirectoryHandle *handle;
1073
handle = (DirectoryHandle *) method_handle;
1076
#ifdef HAVE_READDIR_R
1077
if (readdir_r (handle->dir, handle->current_entry, &result) != 0) {
1078
/* Work around a Solaris bug.
1079
* readdir64_r returns -1 instead of 0 at EOF.
1082
return GNOME_VFS_ERROR_EOF;
1084
return gnome_vfs_result_from_errno ();
1090
result = readdir (handle->dir);
1092
result = g_dir_read_name (handle->dir);
1095
if (result == NULL && errno != 0) {
1096
GnomeVFSResult ret = gnome_vfs_result_from_errno ();
1101
if (result != NULL) {
1102
memcpy (handle->current_entry, result, sizeof (struct dirent));
1108
if (result == NULL) {
1109
return GNOME_VFS_ERROR_EOF;
1113
file_info->name = g_strdup (result->d_name);
1114
strcpy (handle->name_ptr, result->d_name);
1116
file_info->name = g_strdup (result);
1117
strcpy (handle->name_ptr, result);
1119
full_name = handle->name_buffer;
1121
if (handle->options & GNOME_VFS_FILE_INFO_NAME_ONLY) {
1122
return GNOME_VFS_OK;
1125
if (handle->options & GNOME_VFS_FILE_INFO_GET_SELINUX_CONTEXT) {
1127
/* Attempt to get selinux contet, ignore error (see below) */
1128
get_selinux_context(file_info, full_name, handle->options);
1131
if (get_stat_info (file_info, full_name, handle->options, &statbuf) != GNOME_VFS_OK) {
1132
/* Return OK - this should not terminate the directory iteration
1133
* and we will know from the valid_fields that we don't have the
1136
return GNOME_VFS_OK;
1139
if (handle->options & GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS) {
1140
get_access_info (file_info, full_name);
1143
if (handle->options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE) {
1144
get_mime_type (file_info, full_name, handle->options, &statbuf);
1147
if (handle->options & GNOME_VFS_FILE_INFO_GET_ACL) {
1148
file_get_acl (full_name, file_info, &statbuf, context);
1151
return GNOME_VFS_OK;
1154
static GnomeVFSResult
1155
do_get_file_info (GnomeVFSMethod *method,
1157
GnomeVFSFileInfo *file_info,
1158
GnomeVFSFileInfoOptions options,
1159
GnomeVFSContext *context)
1161
GnomeVFSResult result;
1163
struct stat statbuf;
1165
full_name = get_path_from_uri (uri);
1166
if (full_name == NULL)
1167
return GNOME_VFS_ERROR_INVALID_URI;
1169
file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
1171
file_info->name = get_base_from_uri (uri);
1172
g_assert (file_info->name != NULL);
1174
result = get_stat_info (file_info, full_name, options, &statbuf);
1175
if (result != GNOME_VFS_OK) {
1180
if (options & GNOME_VFS_FILE_INFO_GET_SELINUX_CONTEXT) {
1181
get_selinux_context (file_info, full_name, options);
1184
if (options & GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS) {
1185
get_access_info (file_info, full_name);
1188
if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE) {
1189
get_mime_type (file_info, full_name, options, &statbuf);
1192
if (options & GNOME_VFS_FILE_INFO_GET_ACL) {
1193
file_get_acl (full_name, file_info, &statbuf, context);
1198
return GNOME_VFS_OK;
1201
static GnomeVFSResult
1202
do_get_file_info_from_handle (GnomeVFSMethod *method,
1203
GnomeVFSMethodHandle *method_handle,
1204
GnomeVFSFileInfo *file_info,
1205
GnomeVFSFileInfoOptions options,
1206
GnomeVFSContext *context)
1208
FileHandle *file_handle;
1210
struct stat statbuf;
1211
GnomeVFSResult result;
1213
file_handle = (FileHandle *) method_handle;
1215
file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
1217
full_name = get_path_from_uri (file_handle->uri);
1218
if (full_name == NULL) {
1219
return GNOME_VFS_ERROR_INVALID_URI;
1222
file_info->name = get_base_from_uri (file_handle->uri);
1223
g_assert (file_info->name != NULL);
1225
result = get_stat_info_from_handle (file_info, file_handle,
1227
if (result != GNOME_VFS_OK) {
1232
if (options & GNOME_VFS_FILE_INFO_GET_SELINUX_CONTEXT) {
1233
result = get_selinux_context_from_handle (file_info, file_handle);
1234
if (result != GNOME_VFS_OK) {
1240
if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE) {
1241
get_mime_type (file_info, full_name, options, &statbuf);
1244
if (options & GNOME_VFS_FILE_INFO_GET_ACL) {
1245
file_get_acl (full_name, file_info, &statbuf, context);
1250
return GNOME_VFS_OK;
1253
extern char *filesystem_type (char *path, char *relpath, struct stat *statp);
1255
G_LOCK_DEFINE_STATIC (fstype);
1258
do_is_local (GnomeVFSMethod *method,
1259
const GnomeVFSURI *uri)
1261
struct stat statbuf;
1266
g_return_val_if_fail (uri != NULL, FALSE);
1268
path = get_path_from_uri (uri);
1270
return TRUE; /* GNOME_VFS_ERROR_INVALID_URI */
1272
/* Recurse through directories until one of them exists */
1273
while (g_stat (path, &statbuf) != 0) {
1274
gchar *tmp_path = g_path_get_dirname (path);
1280
type = filesystem_type (path, path, &statbuf);
1281
is_local = ((strcmp (type, "nfs") != 0) &&
1282
(strcmp (type, "afs") != 0) &&
1283
(strcmp (type, "autofs") != 0) &&
1284
(strcmp (type, "unknown") != 0) &&
1285
(strcmp (type, "novfs") != 0) &&
1286
(strcmp (type, "ncpfs") != 0));
1294
static GnomeVFSResult
1295
do_make_directory (GnomeVFSMethod *method,
1298
GnomeVFSContext *context)
1303
full_name = get_path_from_uri (uri);
1304
if (full_name == NULL)
1305
return GNOME_VFS_ERROR_INVALID_URI;
1307
retval = g_mkdir (full_name, perm);
1312
return gnome_vfs_result_from_errno ();
1315
return GNOME_VFS_OK;
1318
static GnomeVFSResult
1319
do_remove_directory (GnomeVFSMethod *method,
1321
GnomeVFSContext *context)
1326
full_name = get_path_from_uri (uri);
1327
if (full_name == NULL)
1328
return GNOME_VFS_ERROR_INVALID_URI;
1330
retval = g_rmdir (full_name);
1335
return gnome_vfs_result_from_errno ();
1338
return GNOME_VFS_OK;
1341
#undef DEBUG_FIND_DIRECTORY
1342
/* Get rid of debugging code once we know the logic works. */
1344
#define TRASH_DIRECTORY_NAME_BASE ".Trash"
1345
#define MAX_TRASH_SEARCH_DEPTH 5
1348
* Works like mkdir, except it creates all the levels of directories in @path.
1351
mkdir_recursive (const char *path, int permission_bits)
1353
struct stat stat_buffer;
1354
const char *dir_separator_scanner;
1357
/* try creating a director for each level */
1358
for (dir_separator_scanner = path;; dir_separator_scanner++) {
1359
/* advance to the next directory level */
1360
for (;;dir_separator_scanner++) {
1361
if (!*dir_separator_scanner) {
1364
if (G_IS_DIR_SEPARATOR (*dir_separator_scanner)) {
1368
if (dir_separator_scanner - path > 0) {
1369
current_path = g_strndup (path, dir_separator_scanner - path);
1370
g_mkdir (current_path, permission_bits);
1371
if (g_stat (current_path, &stat_buffer) != 0) {
1372
/* we failed to create a directory and it wasn't there already;
1375
g_free (current_path);
1378
g_free (current_path);
1380
if (!*dir_separator_scanner) {
1389
append_to_path (const char *path, const char *name)
1391
return g_build_filename (path, name, NULL);
1395
append_trash_path (const char *path)
1397
char *per_user_part;
1400
per_user_part = g_strdup_printf ("%s-%lu",
1401
TRASH_DIRECTORY_NAME_BASE,
1402
(unsigned long) geteuid());
1404
retval = g_build_filename (path, per_user_part, "files", NULL);
1406
g_free (per_user_part);
1412
find_trash_in_hierarchy (const char *start_dir, dev_t near_device_id, GnomeVFSContext *context)
1415
struct stat stat_buffer;
1417
if (gnome_vfs_context_check_cancellation (context))
1420
/* check if there is a trash in this directory */
1421
trash_path = append_trash_path (start_dir);
1422
if (g_lstat (trash_path, &stat_buffer) == 0 && S_ISDIR (stat_buffer.st_mode)) {
1423
/* found it, we are done */
1424
g_assert (near_device_id == stat_buffer.st_dev);
1427
g_free (trash_path);
1432
static GList *cached_trash_directories;
1433
G_LOCK_DEFINE_STATIC (cached_trash_directories);
1435
/* Element used to store chached Trash entries in the local, in-memory Trash item cache. */
1438
char *device_mount_point;
1440
} TrashDirectoryCachedItem;
1444
} FindByDeviceIDParameters;
1447
match_trash_item_by_device_id (gconstpointer item, gconstpointer data)
1449
const TrashDirectoryCachedItem *cached_item;
1450
FindByDeviceIDParameters *parameters;
1452
cached_item = (const TrashDirectoryCachedItem *)item;
1453
parameters = (FindByDeviceIDParameters *)data;
1455
return cached_item->device_id == parameters->device_id ? 0 : -1;
1459
try_creating_trash_in (const char *path, guint permissions)
1464
trash_path = append_trash_path (path);
1465
if (mkdir_recursive (trash_path, permissions) == 0) {
1466
#ifdef DEBUG_FIND_DIRECTORY
1467
g_print ("created trash in %s\n", trash_path);
1472
#ifdef DEBUG_FIND_DIRECTORY
1473
g_print ("failed to create trash in %s\n", trash_path);
1475
g_free (trash_path);
1480
find_disk_top_directory (const char *item_on_disk,
1481
dev_t near_device_id,
1482
GnomeVFSContext *context)
1484
char *disk_top_directory;
1485
struct stat stat_buffer;
1487
disk_top_directory = g_strdup (item_on_disk);
1489
/* Walk up in the hierarchy, finding the top-most point that still
1490
* matches our device ID -- the root directory of the volume.
1493
char *previous_search_directory;
1496
previous_search_directory = g_strdup (disk_top_directory);
1497
last_slash = strrchr (disk_top_directory, '/');
1498
if (last_slash == NULL) {
1499
g_free (previous_search_directory);
1504
if (g_stat (disk_top_directory, &stat_buffer) < 0
1505
|| stat_buffer.st_dev != near_device_id) {
1506
/* we ran past the root of the disk we are exploring */
1507
g_free (disk_top_directory);
1508
disk_top_directory = previous_search_directory;
1511
/* FIXME bugzilla.eazel.com 2733: This must result in
1512
* a cancelled error, but there's no way for the
1513
* caller to know that. We probably have to add a
1514
* GnomeVFSResult to this function.
1516
if (gnome_vfs_context_check_cancellation (context)) {
1517
g_free (previous_search_directory);
1518
g_free (disk_top_directory);
1522
return disk_top_directory;
1525
#define TRASH_ENTRY_CACHE_PARENT ".gnome/gnome-vfs"
1526
#define TRASH_ENTRY_CACHE_NAME ".trash_entry_cache"
1527
#define NON_EXISTENT_TRASH_ENTRY "-"
1529
/* Save the localy cached Trashed paths on disk in the user's home
1533
save_trash_entry_cache (void)
1536
char *cache_file_parent, *cache_file_path;
1538
char *buffer, *escaped_path, *escaped_mount_point;
1540
cache_file_parent = append_to_path (gnome_vfs_get_home_dir (), TRASH_ENTRY_CACHE_PARENT);
1541
cache_file_path = append_to_path (cache_file_parent, TRASH_ENTRY_CACHE_NAME);
1543
if (mkdir_recursive (cache_file_parent, 0777) != 0) {
1544
g_warning ("failed to create trash item cache file");
1548
cache_file = g_open (cache_file_path, O_CREAT | O_TRUNC | O_RDWR, 0666);
1549
if (cache_file < 0) {
1550
g_warning ("failed to create trash item cache file");
1554
for (p = cached_trash_directories; p != NULL; p = p->next) {
1555
/* Use proper escaping to not confuse paths with spaces in them */
1556
escaped_path = gnome_vfs_escape_path_string (
1557
((TrashDirectoryCachedItem *)p->data)->path);
1558
escaped_mount_point = gnome_vfs_escape_path_string(
1559
((TrashDirectoryCachedItem *)p->data)->device_mount_point);
1561
buffer = g_strdup_printf ("%s %s\n", escaped_mount_point, escaped_path);
1562
write (cache_file, buffer, strlen (buffer));
1564
#ifdef DEBUG_FIND_DIRECTORY
1565
g_print ("saving trash item cache %s\n", buffer);
1569
g_free (escaped_mount_point);
1570
g_free (escaped_path);
1574
g_free (cache_file_path);
1575
g_free (cache_file_parent);
1579
const char *mount_point;
1580
const char *trash_path;
1583
} UpdateOneCachedEntryContext;
1585
/* Updates one entry in the local Trash item cache to reflect the
1586
* location we just found or in which we created a new Trash.
1589
update_one_cached_trash_entry (gpointer element, gpointer cast_to_context)
1591
UpdateOneCachedEntryContext *context;
1592
TrashDirectoryCachedItem *item;
1594
context = (UpdateOneCachedEntryContext *)cast_to_context;
1595
item = (TrashDirectoryCachedItem *)element;
1597
if (context->done) {
1598
/* We already took care of business in a previous iteration. */
1602
if (strcmp (context->mount_point, item->device_mount_point) == 0) {
1603
/* This is the item we are looking for, update it. */
1604
g_free (item->path);
1605
item->path = g_strdup (context->trash_path);
1606
item->device_id = context->device_id;
1609
context->done = TRUE;
1614
add_local_cached_trash_entry (dev_t near_device_id, const char *trash_path, const char *mount_point)
1616
TrashDirectoryCachedItem *new_cached_item;
1617
UpdateOneCachedEntryContext update_context;
1619
/* First check if we already have an entry for this mountpoint,
1623
update_context.mount_point = mount_point;
1624
update_context.trash_path = trash_path;
1625
update_context.device_id = near_device_id;
1626
update_context.done = FALSE;
1628
g_list_foreach (cached_trash_directories, update_one_cached_trash_entry, &update_context);
1629
if (update_context.done) {
1630
/* Sucessfully updated, no more work left. */
1634
/* Save the new trash item to the local cache. */
1635
new_cached_item = g_new (TrashDirectoryCachedItem, 1);
1636
new_cached_item->path = g_strdup (trash_path);
1637
new_cached_item->device_mount_point = g_strdup (mount_point);
1638
new_cached_item->device_id = near_device_id;
1641
cached_trash_directories = g_list_prepend (cached_trash_directories, new_cached_item);
1645
add_cached_trash_entry (dev_t near_device_id, const char *trash_path, const char *mount_point)
1647
add_local_cached_trash_entry (near_device_id, trash_path, mount_point);
1648
/* write out the local cache */
1649
save_trash_entry_cache ();
1653
destroy_cached_trash_entry (TrashDirectoryCachedItem *entry)
1655
g_free (entry->path);
1656
g_free (entry->device_mount_point);
1660
/* Read the cached entries for the file cache into the local Trash item cache. */
1662
read_saved_cached_trash_entries (void)
1664
char *cache_file_path;
1667
char escaped_mount_point[GET_PATH_MAX()], escaped_trash_path[GET_PATH_MAX()];
1668
char *mount_point, *trash_path;
1669
struct stat stat_buffer;
1670
gboolean removed_item;
1672
/* empty the old locally cached entries */
1673
g_list_foreach (cached_trash_directories,
1674
(GFunc)destroy_cached_trash_entry, NULL);
1675
g_list_free (cached_trash_directories);
1676
cached_trash_directories = NULL;
1678
/* read in the entries from disk */
1679
cache_file_path = g_build_filename (gnome_vfs_get_home_dir (),
1680
TRASH_ENTRY_CACHE_PARENT,
1681
TRASH_ENTRY_CACHE_NAME,
1683
cache_file = g_fopen (cache_file_path, "r");
1685
if (cache_file != NULL) {
1686
removed_item = FALSE;
1688
if (fgets (buffer, sizeof (buffer), cache_file) == NULL) {
1694
if (sscanf (buffer, "%s %s", escaped_mount_point, escaped_trash_path) == 2) {
1695
/* the paths are saved in escaped form */
1696
trash_path = gnome_vfs_unescape_string (escaped_trash_path, DIR_SEPARATORS);
1697
mount_point = gnome_vfs_unescape_string (escaped_mount_point, DIR_SEPARATORS);
1699
if (trash_path != NULL
1700
&& mount_point != NULL
1701
&& (strcmp (trash_path, NON_EXISTENT_TRASH_ENTRY) != 0 && g_lstat (trash_path, &stat_buffer) == 0)
1702
&& g_stat (mount_point, &stat_buffer) == 0) {
1703
/* We know the trash exist and we checked that it's really
1704
* there - this is a good entry, copy it into the local cache.
1705
* We don't want to rely on old non-existing trash entries, as they
1706
* could have changed now, and they stick around filling up the cache,
1707
* and slowing down startup.
1709
add_local_cached_trash_entry (stat_buffer.st_dev, trash_path, mount_point);
1710
#ifdef DEBUG_FIND_DIRECTORY
1711
g_print ("read trash item cache entry %s %s\n", trash_path, mount_point);
1714
removed_item = TRUE;
1718
g_free (trash_path);
1719
g_free (mount_point);
1721
fclose (cache_file);
1722
/* Save cache to get rid of stuff from on-disk cache */
1724
save_trash_entry_cache ();
1728
g_free (cache_file_path);
1731
/* Create a Trash directory on the same disk as @full_name_near. */
1733
create_trash_near (const char *full_name_near, dev_t near_device_id, const char *disk_top_directory,
1734
guint permissions, GnomeVFSContext *context)
1736
return try_creating_trash_in (disk_top_directory, permissions);
1741
cached_trash_entry_exists (const TrashDirectoryCachedItem *entry)
1743
struct stat stat_buffer;
1744
return g_lstat (entry->path, &stat_buffer) == 0;
1747
/* Search through the local cache looking for an entry that matches a given
1748
* device ID. If @check_disk specified, check if the entry we found actually exists.
1751
find_locally_cached_trash_entry_for_device_id (dev_t device_id, gboolean check_disk)
1753
GList *matching_item;
1754
FindByDeviceIDParameters tmp;
1755
const char *trash_path;
1757
tmp.device_id = device_id;
1759
matching_item = g_list_find_custom (cached_trash_directories,
1760
&tmp, match_trash_item_by_device_id);
1762
if (matching_item == NULL) {
1766
trash_path = ((TrashDirectoryCachedItem *)matching_item->data)->path;
1768
if (trash_path == NULL) {
1769
/* we already know that this disk does not contain a trash directory */
1770
#ifdef DEBUG_FIND_DIRECTORY
1771
g_print ("cache indicates no trash for %s \n", trash_path);
1773
return g_strdup (NON_EXISTENT_TRASH_ENTRY);
1777
/* We found something, make sure it still exists. */
1778
if (strcmp (((TrashDirectoryCachedItem *)matching_item->data)->path, NON_EXISTENT_TRASH_ENTRY) != 0
1779
&& !cached_trash_entry_exists ((TrashDirectoryCachedItem *)matching_item->data)) {
1780
/* The cached item doesn't really exist, make a new one
1781
* and delete the cached entry
1783
#ifdef DEBUG_FIND_DIRECTORY
1784
g_print ("entry %s doesn't exist, removing \n",
1785
((TrashDirectoryCachedItem *)matching_item->data)->path);
1787
destroy_cached_trash_entry ((TrashDirectoryCachedItem *)matching_item->data);
1788
cached_trash_directories = g_list_remove (cached_trash_directories,
1789
matching_item->data);
1794
#ifdef DEBUG_FIND_DIRECTORY
1795
g_print ("local cache found %s \n", trash_path);
1797
g_assert (matching_item != NULL);
1798
return g_strdup (trash_path);
1801
/* Look for an entry in the file and local caches. */
1803
find_cached_trash_entry_for_device (dev_t device_id, gboolean check_disk)
1805
if (cached_trash_directories == NULL) {
1809
read_saved_cached_trash_entries ();
1811
return find_locally_cached_trash_entry_for_device_id (device_id, check_disk);
1814
/* Search for a Trash entry or create one. Called when there is no cached entry. */
1816
find_or_create_trash_near (const char *full_name_near, dev_t near_device_id,
1817
gboolean create_if_needed, gboolean find_if_needed, guint permissions,
1818
GnomeVFSContext *context)
1821
char *disk_top_directory;
1824
/* figure out the topmost disk directory */
1825
disk_top_directory = find_disk_top_directory (full_name_near,
1826
near_device_id, context);
1828
if (disk_top_directory == NULL) {
1829
/* Failed to find it, don't look at this disk until we
1830
* are ready to try to create a Trash on it again.
1832
#ifdef DEBUG_FIND_DIRECTORY
1833
g_print ("failed to find top disk directory for %s\n", full_name_near);
1835
add_cached_trash_entry (near_device_id, NON_EXISTENT_TRASH_ENTRY, disk_top_directory);
1839
if (find_if_needed) {
1840
/* figure out the topmost disk directory */
1841
result = find_trash_in_hierarchy (disk_top_directory, near_device_id, context);
1842
if (result == NULL) {
1843
/* We just found out there is no Trash on the disk,
1844
* remember this for next time.
1846
result = g_strdup(NON_EXISTENT_TRASH_ENTRY);
1850
if (result == NULL && create_if_needed) {
1851
/* didn't find a Trash, create one */
1852
result = create_trash_near (full_name_near, near_device_id, disk_top_directory,
1853
permissions, context);
1856
if (result != NULL) {
1857
/* remember whatever we found for next time */
1858
add_cached_trash_entry (near_device_id, result, disk_top_directory);
1861
g_free (disk_top_directory);
1866
/* Find or create a trash directory on the same disk as @full_name_near. Check
1867
* the local and file cache for matching Trash entries first.
1869
* This is the only entry point for the trash cache code,
1870
* we holds the lock while operating on it only here.
1873
find_trash_directory (const char *full_name_near, dev_t near_device_id,
1874
gboolean create_if_needed, gboolean find_if_needed,
1875
guint permissions, GnomeVFSContext *context)
1879
G_LOCK (cached_trash_directories);
1881
/* look in the saved trash locations first */
1882
result = find_cached_trash_entry_for_device (near_device_id, find_if_needed);
1884
if (find_if_needed) {
1885
if (result != NULL && strcmp (result, NON_EXISTENT_TRASH_ENTRY) == 0 && create_if_needed) {
1886
/* We know there is no Trash yet because we remember
1887
* from the last time we looked.
1888
* If we were asked to create one, ignore the fact that
1889
* we already looked for it, look again and create a
1890
* new trash if we find nothing.
1892
#ifdef DEBUG_FIND_DIRECTORY
1893
g_print ("cache indicates no trash for %s, force a creation \n", full_name_near);
1899
if (result == NULL) {
1900
/* No luck sofar. Look for the Trash on the disk, optionally create it
1901
* if we find nothing.
1903
result = find_or_create_trash_near (full_name_near, near_device_id,
1904
create_if_needed, find_if_needed, permissions, context);
1906
} else if (create_if_needed) {
1907
if (result == NULL || strcmp (result, NON_EXISTENT_TRASH_ENTRY) == 0) {
1908
result = find_or_create_trash_near (full_name_near, near_device_id,
1909
create_if_needed, find_if_needed, permissions, context);
1913
if (result != NULL && strcmp(result, NON_EXISTENT_TRASH_ENTRY) == 0) {
1914
/* This means that we know there is no Trash */
1919
G_UNLOCK (cached_trash_directories);
1924
static GnomeVFSResult
1925
do_find_directory (GnomeVFSMethod *method,
1926
GnomeVFSURI *near_uri,
1927
GnomeVFSFindDirectoryKind kind,
1928
GnomeVFSURI **result_uri,
1929
gboolean create_if_needed,
1930
gboolean find_if_needed,
1932
GnomeVFSContext *context)
1935
char *full_name_near;
1936
struct stat near_item_stat;
1937
struct stat home_volume_stat;
1938
const char *home_directory;
1939
char *target_directory_path;
1940
char *target_directory_uri;
1943
target_directory_path = NULL;
1946
full_name_near = get_path_from_uri (near_uri);
1947
if (full_name_near == NULL)
1948
return GNOME_VFS_ERROR_INVALID_URI;
1950
/* We will need the URI and the stat structure for the home directory. */
1951
home_directory = gnome_vfs_get_home_dir ();
1953
if (gnome_vfs_context_check_cancellation (context)) {
1954
g_free (full_name_near);
1955
return GNOME_VFS_ERROR_CANCELLED;
1958
retval = g_lstat (full_name_near, &near_item_stat);
1960
g_free (full_name_near);
1961
return gnome_vfs_result_from_errno ();
1964
if (gnome_vfs_context_check_cancellation (context)) {
1965
g_free (full_name_near);
1966
return GNOME_VFS_ERROR_CANCELLED;
1969
retval = g_stat (home_directory, &home_volume_stat);
1971
g_free (full_name_near);
1972
return gnome_vfs_result_from_errno ();
1975
if (gnome_vfs_context_check_cancellation (context)) {
1976
g_free (full_name_near);
1977
return GNOME_VFS_ERROR_CANCELLED;
1981
case GNOME_VFS_DIRECTORY_KIND_TRASH:
1982
/* Use 0700 (S_IRWXU) for the permissions,
1983
* regardless of the requested permissions, so other
1984
* users can't view the trash files.
1986
permissions = S_IRWXU;
1987
if (near_item_stat.st_dev != home_volume_stat.st_dev) {
1988
/* This volume does not contain our home, we have to find/create the Trash
1989
* elsewhere on the volume. Use a heuristic to find a good place.
1991
if (gnome_vfs_context_check_cancellation (context))
1992
return GNOME_VFS_ERROR_CANCELLED;
1994
target_directory_path = find_trash_directory (full_name_near,
1995
near_item_stat.st_dev, create_if_needed, find_if_needed,
1996
permissions, context);
1998
if (gnome_vfs_context_check_cancellation (context)) {
1999
return GNOME_VFS_ERROR_CANCELLED;
2002
/* volume with a home directory, just create a trash in home */
2003
target_directory_path = g_build_filename (g_get_user_data_dir (), "Trash", "files", NULL);
2007
case GNOME_VFS_DIRECTORY_KIND_DESKTOP:
2008
if (near_item_stat.st_dev != home_volume_stat.st_dev) {
2012
target_directory_path = append_to_path (home_directory, "Desktop");
2019
g_free (full_name_near);
2021
if (target_directory_path == NULL) {
2022
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2025
if (create_if_needed && g_access (target_directory_path, F_OK) != 0) {
2026
mkdir_recursive (target_directory_path, permissions);
2029
if (g_access (target_directory_path, F_OK) != 0) {
2030
g_free (target_directory_path);
2031
return GNOME_VFS_ERROR_NOT_FOUND;
2034
target_directory_uri = gnome_vfs_get_uri_from_local_path (target_directory_path);
2035
g_free (target_directory_path);
2036
*result_uri = gnome_vfs_uri_new (target_directory_uri);
2037
g_free (target_directory_uri);
2039
return GNOME_VFS_OK;
2042
static GnomeVFSResult
2043
rename_helper (const gchar *old_full_name,
2044
const gchar *new_full_name,
2045
gboolean force_replace,
2046
GnomeVFSContext *context)
2048
gboolean old_exists;
2049
struct stat statbuf;
2052
GnomeVFSHandle *temp_handle;
2053
GnomeVFSResult result;
2055
retval = g_stat (new_full_name, &statbuf);
2057
/* Special case for files on case insensitive (vfat) filesystems:
2058
* If the old and the new name only differ by case,
2059
* try renaming via a temp file name.
2061
if (g_ascii_strcasecmp (old_full_name, new_full_name) == 0
2062
&& strcmp (old_full_name, new_full_name) != 0 && ! force_replace) {
2064
if (gnome_vfs_context_check_cancellation (context))
2065
return GNOME_VFS_ERROR_CANCELLED;
2067
result = gnome_vfs_create_temp (old_full_name, &temp_name, &temp_handle);
2068
if (result != GNOME_VFS_OK)
2070
gnome_vfs_close (temp_handle);
2071
g_unlink (temp_name);
2073
retval = g_rename (old_full_name, temp_name);
2075
if (g_stat (new_full_name, &statbuf) != 0
2076
&& g_rename (temp_name, new_full_name) == 0) {
2078
return GNOME_VFS_OK;
2080
/* Revert the filename back to original */
2081
retval = g_rename (temp_name, old_full_name);
2083
return GNOME_VFS_ERROR_FILE_EXISTS;
2086
return gnome_vfs_result_from_errno_code (retval);
2088
} else if (! force_replace) {
2089
/* If we are not allowed to replace an existing file,
2092
return GNOME_VFS_ERROR_FILE_EXISTS;
2099
if (gnome_vfs_context_check_cancellation (context))
2100
return GNOME_VFS_ERROR_CANCELLED;
2103
if (force_replace && old_exists)
2104
g_remove (new_full_name);
2107
retval = g_rename (old_full_name, new_full_name);
2110
/* FIXME bugzilla.eazel.com 1186: The following assumes that,
2111
* if `new_uri' and `old_uri' are on different file systems,
2112
* `rename()' will always return `EXDEV' instead of `EISDIR',
2113
* even if the old file is not a directory while the new one
2114
* is. If this is not the case, we have to stat() both the
2117
if (retval != 0 && errno == EISDIR && force_replace && old_exists) {
2118
/* The Unix version of `rename()' fails if the original file is
2119
not a directory, while the new one is. But we have been
2120
explicitly asked to replace the destination name, so if the
2121
new name points to a directory, we remove it manually. */
2122
if (S_ISDIR (statbuf.st_mode)) {
2123
if (gnome_vfs_context_check_cancellation (context))
2124
return GNOME_VFS_ERROR_CANCELLED;
2125
retval = g_rmdir (new_full_name);
2127
return gnome_vfs_result_from_errno ();
2130
if (gnome_vfs_context_check_cancellation (context))
2131
return GNOME_VFS_ERROR_CANCELLED;
2133
retval = g_rename (old_full_name, new_full_name);
2138
return gnome_vfs_result_from_errno ();
2141
return GNOME_VFS_OK;
2144
static GnomeVFSResult
2145
set_symlink_name_helper (const gchar *full_name,
2146
const GnomeVFSFileInfo *info)
2149
struct stat statbuf;
2151
if (info->symlink_name == NULL) {
2152
return GNOME_VFS_ERROR_BAD_PARAMETERS;
2155
if (g_lstat (full_name, &statbuf) != 0) {
2156
return gnome_vfs_result_from_errno ();
2159
if (!S_ISLNK (statbuf.st_mode)) {
2160
return GNOME_VFS_ERROR_NOT_A_SYMBOLIC_LINK;
2163
if (g_unlink (full_name) != 0) {
2164
return gnome_vfs_result_from_errno ();
2167
if (symlink (info->symlink_name, full_name) != 0) {
2168
/* TODO should we try to restore the old symlink? */
2169
return gnome_vfs_result_from_errno ();
2172
return GNOME_VFS_OK;
2174
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2179
static GnomeVFSResult
2180
do_move (GnomeVFSMethod *method,
2181
GnomeVFSURI *old_uri,
2182
GnomeVFSURI *new_uri,
2183
gboolean force_replace,
2184
GnomeVFSContext *context)
2186
gchar *old_full_name;
2187
gchar *new_full_name;
2188
GnomeVFSResult result;
2190
old_full_name = get_path_from_uri (old_uri);
2191
if (old_full_name == NULL)
2192
return GNOME_VFS_ERROR_INVALID_URI;
2194
new_full_name = get_path_from_uri (new_uri);
2195
if (new_full_name == NULL) {
2196
g_free (old_full_name);
2197
return GNOME_VFS_ERROR_INVALID_URI;
2200
result = rename_helper (old_full_name, new_full_name,
2201
force_replace, context);
2203
g_free (old_full_name);
2204
g_free (new_full_name);
2209
static GnomeVFSResult
2210
do_unlink (GnomeVFSMethod *method,
2212
GnomeVFSContext *context)
2217
full_name = get_path_from_uri (uri);
2218
if (full_name == NULL) {
2219
return GNOME_VFS_ERROR_INVALID_URI;
2222
retval = g_unlink (full_name);
2227
return gnome_vfs_result_from_errno ();
2230
return GNOME_VFS_OK;
2233
static GnomeVFSResult
2234
do_create_symbolic_link (GnomeVFSMethod *method,
2236
const char *target_reference,
2237
GnomeVFSContext *context)
2240
const char *link_scheme, *target_scheme;
2241
char *link_full_name, *target_full_name;
2242
GnomeVFSResult result;
2243
GnomeVFSURI *target_uri;
2245
g_assert (target_reference != NULL);
2246
g_assert (uri != NULL);
2248
/* what we actually want is a function that takes a const char * and
2249
* tells whether it is a valid URI
2251
target_uri = gnome_vfs_uri_new (target_reference);
2252
if (target_uri == NULL) {
2253
return GNOME_VFS_ERROR_INVALID_URI;
2256
link_scheme = gnome_vfs_uri_get_scheme (uri);
2257
g_assert (link_scheme != NULL);
2259
target_scheme = gnome_vfs_uri_get_scheme (target_uri);
2260
if (target_scheme == NULL) {
2261
target_scheme = "file";
2264
if ((strcmp (link_scheme, "file") == 0) && (strcmp (target_scheme, "file") == 0)) {
2265
/* symlink between two places on the local filesystem */
2266
if (strncmp (target_reference, "file", 4) != 0) {
2267
/* target_reference wasn't a full URI */
2268
target_full_name = strdup (target_reference);
2270
target_full_name = get_path_from_uri (target_uri);
2273
link_full_name = get_path_from_uri (uri);
2275
if (symlink (target_full_name, link_full_name) != 0) {
2276
result = gnome_vfs_result_from_errno ();
2278
result = GNOME_VFS_OK;
2281
g_free (target_full_name);
2282
g_free (link_full_name);
2284
/* FIXME bugzilla.eazel.com 2792: do a URI link */
2285
result = GNOME_VFS_ERROR_NOT_SUPPORTED;
2288
gnome_vfs_uri_unref (target_uri);
2292
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2296
/* When checking whether two locations are on the same file system, we are
2297
doing this to determine whether we can recursively move or do other
2298
sorts of transfers. When a symbolic link is the "source", its
2299
location is the location of the link file, because we want to
2300
know about transferring the link, whereas for symbolic links that
2301
are "targets", we use the location of the object being pointed to,
2302
because that is where we will be moving/copying to. */
2303
static GnomeVFSResult
2304
do_check_same_fs (GnomeVFSMethod *method,
2305
GnomeVFSURI *source_uri,
2306
GnomeVFSURI *target_uri,
2307
gboolean *same_fs_return,
2308
GnomeVFSContext *context)
2310
gchar *full_name_source, *full_name_target;
2311
struct stat s_source, s_target;
2314
full_name_source = get_path_from_uri (source_uri);
2315
retval = g_lstat (full_name_source, &s_source);
2316
g_free (full_name_source);
2319
return gnome_vfs_result_from_errno ();
2321
if (gnome_vfs_context_check_cancellation (context))
2322
return GNOME_VFS_ERROR_CANCELLED;
2324
full_name_target = get_path_from_uri (target_uri);
2325
retval = g_stat (full_name_target, &s_target);
2326
g_free (full_name_target);
2329
return gnome_vfs_result_from_errno ();
2331
*same_fs_return = (s_source.st_dev == s_target.st_dev);
2333
return GNOME_VFS_OK;
2336
static GnomeVFSResult
2337
do_set_file_info (GnomeVFSMethod *method,
2339
const GnomeVFSFileInfo *info,
2340
GnomeVFSSetFileInfoMask mask,
2341
GnomeVFSContext *context)
2345
full_name = get_path_from_uri (uri);
2346
if (full_name == NULL)
2347
return GNOME_VFS_ERROR_INVALID_URI;
2349
if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
2350
GnomeVFSResult result;
2351
gchar *dir, *encoded_dir;
2354
encoded_dir = gnome_vfs_uri_extract_dirname (uri);
2355
dir = gnome_vfs_unescape_string (encoded_dir, DIR_SEPARATORS);
2356
g_free (encoded_dir);
2357
g_assert (dir != NULL);
2359
new_name = g_build_filename (dir, info->name, NULL);
2361
result = rename_helper (full_name, new_name, FALSE, context);
2365
full_name = new_name;
2367
if (result != GNOME_VFS_OK) {
2373
if (mask & GNOME_VFS_SET_FILE_INFO_SELINUX_CONTEXT) {
2374
GnomeVFSResult result = set_selinux_context(info, full_name);
2381
if (gnome_vfs_context_check_cancellation (context)) {
2383
return GNOME_VFS_ERROR_CANCELLED;
2386
if (mask & GNOME_VFS_SET_FILE_INFO_PERMISSIONS) {
2387
if (chmod (full_name, info->permissions) != 0) {
2389
return gnome_vfs_result_from_errno ();
2393
if (gnome_vfs_context_check_cancellation (context)) {
2395
return GNOME_VFS_ERROR_CANCELLED;
2398
if (mask & GNOME_VFS_SET_FILE_INFO_OWNER) {
2400
if (chown (full_name, info->uid, info->gid) != 0) {
2402
return gnome_vfs_result_from_errno ();
2405
g_warning ("Not implemented: GNOME_VFS_SET_FILE_INFO_OWNER");
2409
if (gnome_vfs_context_check_cancellation (context)) {
2411
return GNOME_VFS_ERROR_CANCELLED;
2414
if (mask & GNOME_VFS_SET_FILE_INFO_TIME) {
2415
struct utimbuf utimbuf;
2417
utimbuf.actime = info->atime;
2418
utimbuf.modtime = info->mtime;
2420
if (utime (full_name, &utimbuf) != 0) {
2422
return gnome_vfs_result_from_errno ();
2426
if (gnome_vfs_context_check_cancellation (context)) {
2428
return GNOME_VFS_ERROR_CANCELLED;
2431
if (mask & GNOME_VFS_SET_FILE_INFO_ACL) {
2432
GnomeVFSResult result;
2434
result = file_set_acl (full_name, info, context);
2435
if (result != GNOME_VFS_OK) {
2441
if (mask & GNOME_VFS_SET_FILE_INFO_SYMLINK_NAME) {
2442
GnomeVFSResult result;
2444
result = set_symlink_name_helper (full_name, info);
2445
if (result != GNOME_VFS_OK) {
2453
return GNOME_VFS_OK;
2458
fam_do_iter_unlocked (void)
2460
while (fam_connection != NULL && FAMPending(fam_connection)) {
2462
FileMonitorHandle *handle;
2464
GnomeVFSMonitorEventType event_type;
2466
if (FAMNextEvent(fam_connection, &ev) != 1) {
2467
FAMClose(fam_connection);
2468
g_free(fam_connection);
2469
g_source_remove (fam_watch_id);
2471
fam_connection = NULL;
2475
handle = (FileMonitorHandle *)ev.userdata;
2476
cancelled = handle->cancelled;
2481
event_type = GNOME_VFS_MONITOR_EVENT_CHANGED;
2484
event_type = GNOME_VFS_MONITOR_EVENT_DELETED;
2486
case FAMStartExecuting:
2487
event_type = GNOME_VFS_MONITOR_EVENT_STARTEXECUTING;
2489
case FAMStopExecuting:
2490
event_type = GNOME_VFS_MONITOR_EVENT_STOPEXECUTING;
2493
event_type = GNOME_VFS_MONITOR_EVENT_CREATED;
2495
case FAMAcknowledge:
2496
if (handle->cancelled) {
2497
gnome_vfs_uri_unref (handle->uri);
2508
if (event_type != -1 && !cancelled) {
2509
GnomeVFSURI *info_uri;
2513
* FAM can send events with either a absolute or
2514
* relative (from the monitored URI) path, so check if
2515
* the filename starts with '/'.
2517
if (ev.filename[0] == '/') {
2518
info_str = gnome_vfs_get_uri_from_local_path (ev.filename);
2519
info_uri = gnome_vfs_uri_new (info_str);
2522
info_uri = gnome_vfs_uri_append_file_name (handle->uri, ev.filename);
2524
/* This queues an idle, so there are no reentrancy issues */
2525
gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
2528
gnome_vfs_uri_unref (info_uri);
2536
fam_callback (GIOChannel *source,
2537
GIOCondition condition,
2541
G_LOCK (fam_connection);
2543
res = fam_do_iter_unlocked ();
2545
G_UNLOCK (fam_connection);
2553
monitor_setup (void)
2557
G_LOCK (fam_connection);
2559
if (fam_connection == NULL) {
2560
fam_connection = g_malloc0(sizeof(FAMConnection));
2561
if (FAMOpen2(fam_connection, "gnome-vfs user") != 0) {
2563
g_print ("FAMOpen failed, FAMErrno=%d\n", FAMErrno);
2565
g_free(fam_connection);
2566
fam_connection = NULL;
2567
G_UNLOCK (fam_connection);
2570
ioc = g_io_channel_unix_new (FAMCONNECTION_GETFD(fam_connection));
2571
fam_watch_id = g_io_add_watch (ioc,
2572
G_IO_IN | G_IO_HUP | G_IO_ERR,
2573
fam_callback, fam_connection);
2574
g_io_channel_unref (ioc);
2577
G_UNLOCK (fam_connection);
2584
static GnomeVFSResult
2585
fam_monitor_cancel (GnomeVFSMethod *method,
2586
GnomeVFSMethodHandle *method_handle)
2588
FileMonitorHandle *handle = (FileMonitorHandle *)method_handle;
2590
if (!monitor_setup ()) {
2591
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2594
if (handle->cancelled)
2595
return GNOME_VFS_OK;
2597
handle->cancelled = TRUE;
2598
G_LOCK (fam_connection);
2600
/* We need to queue up incoming messages to avoid blocking on write
2601
if there are many monitors being canceled */
2602
fam_do_iter_unlocked ();
2604
if (fam_connection == NULL) {
2605
G_UNLOCK (fam_connection);
2606
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2609
FAMCancelMonitor (fam_connection, &handle->request);
2610
G_UNLOCK (fam_connection);
2612
return GNOME_VFS_OK;
2616
static GnomeVFSResult
2617
fam_monitor_add (GnomeVFSMethod *method,
2618
GnomeVFSMethodHandle **method_handle_return,
2620
GnomeVFSMonitorType monitor_type)
2622
FileMonitorHandle *handle;
2625
if (!monitor_setup ()) {
2626
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2629
filename = get_path_from_uri (uri);
2630
if (filename == NULL) {
2631
return GNOME_VFS_ERROR_INVALID_URI;
2634
handle = g_new0 (FileMonitorHandle, 1);
2635
handle->cancel_func = fam_monitor_cancel;
2637
handle->cancelled = FALSE;
2638
gnome_vfs_uri_ref (uri);
2640
G_LOCK (fam_connection);
2641
/* We need to queue up incoming messages to avoid blocking on write
2642
if there are many monitors being added */
2643
fam_do_iter_unlocked ();
2645
if (fam_connection == NULL) {
2646
G_UNLOCK (fam_connection);
2648
gnome_vfs_uri_unref (uri);
2650
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2653
if (monitor_type == GNOME_VFS_MONITOR_FILE) {
2654
FAMMonitorFile (fam_connection, filename,
2655
&handle->request, handle);
2657
FAMMonitorDirectory (fam_connection, filename,
2658
&handle->request, handle);
2661
G_UNLOCK (fam_connection);
2663
*method_handle_return = (GnomeVFSMethodHandle *)handle;
2667
return GNOME_VFS_OK;
2674
static GnomeVFSResult
2675
inotify_monitor_cancel (GnomeVFSMethod *method,
2676
GnomeVFSMethodHandle *method_handle)
2678
ih_sub_t *sub = (ih_sub_t *)method_handle;
2681
return GNOME_VFS_OK;
2683
ih_sub_cancel (sub);
2685
return GNOME_VFS_OK;
2689
static GnomeVFSResult
2690
inotify_monitor_add (GnomeVFSMethod *method,
2691
GnomeVFSMethodHandle **method_handle_return,
2693
GnomeVFSMonitorType monitor_type)
2697
sub = ih_sub_new (uri, monitor_type);
2699
return GNOME_VFS_ERROR_INVALID_URI;
2701
sub->cancel_func = inotify_monitor_cancel;
2702
if (ih_sub_add (sub) == FALSE) {
2704
*method_handle_return = NULL;
2705
return GNOME_VFS_ERROR_INVALID_URI;
2708
*method_handle_return = (GnomeVFSMethodHandle *)sub;
2709
return GNOME_VFS_OK;
2714
static GnomeVFSResult
2715
do_monitor_add (GnomeVFSMethod *method,
2716
GnomeVFSMethodHandle **method_handle_return,
2718
GnomeVFSMonitorType monitor_type)
2721
/* For remote files, always prefer FAM */
2722
if (do_is_local (method, uri) && ih_startup ()) {
2723
return inotify_monitor_add (method, method_handle_return, uri, monitor_type);
2727
return fam_monitor_add (method, method_handle_return, uri, monitor_type);
2729
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2732
static GnomeVFSResult
2733
do_monitor_cancel (GnomeVFSMethod *method,
2734
GnomeVFSMethodHandle *method_handle)
2736
AnyFileMonitorHandle *handle;
2738
handle = (AnyFileMonitorHandle *)method_handle;
2739
if (handle != NULL) {
2740
return (handle->cancel_func) (method, method_handle);
2742
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2745
static GnomeVFSResult
2746
do_file_control (GnomeVFSMethod *method,
2747
GnomeVFSMethodHandle *method_handle,
2748
const char *operation,
2749
gpointer operation_data,
2750
GnomeVFSContext *context)
2752
if (strcmp (operation, "file:test") == 0) {
2753
*(char **)operation_data = g_strdup ("test ok");
2754
return GNOME_VFS_OK;
2756
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2759
static GnomeVFSResult
2760
do_get_volume_free_space (GnomeVFSMethod *method,
2761
const GnomeVFSURI *uri,
2762
GnomeVFSFileSize *free_space)
2765
GnomeVFSFileSize free_blocks, block_size;
2768
char *unescaped_path;
2771
struct statvfs statfs_buffer;
2773
struct statfs statfs_buffer;
2778
path = gnome_vfs_uri_get_path (uri);
2779
if (path == NULL || *path != '/') {
2780
return GNOME_VFS_ERROR_INVALID_URI;
2783
unescaped_path = gnome_vfs_unescape_string (path, G_DIR_SEPARATOR_S);
2786
statfs_result = statvfs (unescaped_path, &statfs_buffer);
2787
block_size = statfs_buffer.f_frsize;
2789
#if STATFS_ARGS == 2
2790
statfs_result = statfs (unescaped_path, &statfs_buffer);
2791
#elif STATFS_ARGS == 4
2792
statfs_result = statfs (unescaped_path, &statfs_buffer,
2793
sizeof (statfs_buffer), 0);
2795
block_size = statfs_buffer.f_bsize;
2798
if (statfs_result != 0) {
2799
g_free (unescaped_path);
2800
return gnome_vfs_result_from_errno ();
2804
/* CF: I assume ncpfs is linux specific, if you are on a non-linux platform
2805
* where ncpfs is available, please file a bug about it on bugzilla.gnome.org
2808
#if defined(__linux__)
2809
/* ncpfs does not know the amount of available and free space */
2810
if (statfs_buffer.f_bavail == 0 && statfs_buffer.f_bfree == 0) {
2811
/* statvfs does not contain an f_type field, we try again
2814
struct statfs statfs_buffer2;
2815
statfs_result = statfs (unescaped_path, &statfs_buffer2);
2816
g_free (unescaped_path);
2818
if (statfs_result != 0) {
2819
return gnome_vfs_result_from_errno ();
2822
/* linux/ncp_fs.h: NCP_SUPER_MAGIC == 0x564c */
2823
if (statfs_buffer2.f_type == 0x564c)
2825
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2828
/* everything is copacetic... free the unescaped path */
2829
g_free (unescaped_path);
2832
g_free (unescaped_path);
2834
free_blocks = statfs_buffer.f_bavail;
2836
*free_space = block_size * free_blocks;
2838
return GNOME_VFS_OK;
2839
#else /* G_OS_WIN32 */
2840
g_warning ("Not implemented for WIN32: file-method.c:do_get_volume_free_space()");
2841
return GNOME_VFS_ERROR_NOT_SUPPORTED;
2846
static GnomeVFSMethod method = {
2847
sizeof (GnomeVFSMethod),
2860
do_get_file_info_from_handle,
2863
do_remove_directory,
2870
do_create_symbolic_link,
2875
do_get_volume_free_space
2879
vfs_module_init (const char *method_name, const char *args)
2885
vfs_module_shutdown (GnomeVFSMethod *method)