~ubuntu-branches/ubuntu/oneiric/gnome-vfs/oneiric

« back to all changes in this revision

Viewing changes to .pc/30_nfs4.patch/modules/file-method.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell
  • Date: 2010-11-25 15:30:01 UTC
  • mfrom: (0.2.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20101125153001-0jr0wdxh5hjq6asw
Tags: 1:2.24.4-0ubuntu1
* New upstream release
* debian/control:
  - Drop build-depends on quit
  - Build-depend on dh-autoreconf, gnome-common
  - Use standards-version 3.9.1
* debian/rules:
  - Drop patchsys-quilt.mk
  - Use autoreconf.mk
* debian/source:
  - Use source format 3.0
* debian/patches/90_relibtoolize.patch:
  - Dropped, now use dh-autoreconf

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
3
   System.
 
4
 
 
5
   Copyright (C) 1999 Free Software Foundation
 
6
 
 
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.
 
11
 
 
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.
 
16
 
 
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.
 
21
 
 
22
   Authors: 
 
23
        Ettore Perazzoli <ettore@comm2000.it>
 
24
        Pavel Cisler <pavel@eazel.com>
 
25
 */
 
26
 
 
27
#include <config.h>
 
28
 
 
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>
 
40
#include <glib.h>
 
41
#include <glib/gi18n-lib.h>
 
42
#include <glib/gstdio.h>
 
43
#ifndef G_OS_WIN32
 
44
#include <dirent.h>
 
45
#endif
 
46
#include <errno.h>
 
47
#include <fcntl.h>
 
48
#include <limits.h>
 
49
#include <stdio.h>
 
50
#include <stdlib.h>
 
51
#include <string.h>
 
52
#include <sys/stat.h>
 
53
#include <sys/types.h>
 
54
#ifndef G_OS_WIN32              /* We don't want the ftruncate() in mingw's unistd.h */
 
55
#include <unistd.h>
 
56
#endif
 
57
#include <utime.h>
 
58
#include <string.h>
 
59
#ifdef HAVE_FAM
 
60
#include <fam.h>
 
61
#endif
 
62
 
 
63
#ifdef HAVE_SELINUX
 
64
#include <selinux/selinux.h>
 
65
#endif
 
66
 
 
67
#if defined(HAVE_LINUX_INOTIFY_H) || defined(HAVE_SYS_INOTIFY_H)
 
68
#define USE_INOTIFY 1
 
69
#include "inotify-helper.h"
 
70
#endif
 
71
 
 
72
#if HAVE_SYS_STATVFS_H
 
73
#include <sys/statvfs.h>
 
74
#endif
 
75
#if HAVE_SYS_VFS_H
 
76
#include <sys/vfs.h>
 
77
#elif HAVE_SYS_MOUNT_H
 
78
#if HAVE_SYS_PARAM_H
 
79
#include <sys/param.h>
 
80
#endif
 
81
#include <sys/mount.h>
 
82
#endif
 
83
 
 
84
#include "file-method-acl.h"
 
85
 
 
86
#ifdef G_OS_WIN32
 
87
#include <windows.h>
 
88
#endif
 
89
 
 
90
#ifdef G_OS_WIN32
 
91
#define DIR_SEPARATORS "/\\"
 
92
#else
 
93
#define DIR_SEPARATORS "/"
 
94
#endif
 
95
 
 
96
typedef struct {
 
97
        GnomeVFSMethodMonitorCancelFunc cancel_func;  /* Must be first */
 
98
} AnyFileMonitorHandle;
 
99
 
 
100
#ifdef HAVE_FAM
 
101
static FAMConnection *fam_connection = NULL;
 
102
static gint fam_watch_id = 0;
 
103
G_LOCK_DEFINE_STATIC (fam_connection);
 
104
 
 
105
typedef struct {
 
106
        GnomeVFSMethodMonitorCancelFunc cancel_func;  /* Must be first */
 
107
        GnomeVFSURI *uri;
 
108
        FAMRequest request;
 
109
        gboolean     cancelled;
 
110
} FileMonitorHandle;
 
111
#endif
 
112
 
 
113
#ifdef PATH_MAX
 
114
#define GET_PATH_MAX()  PATH_MAX
 
115
#else
 
116
static int
 
117
GET_PATH_MAX (void)
 
118
{
 
119
        static unsigned int value;
 
120
 
 
121
        /* This code is copied from GNU make.  It returns the maximum
 
122
           path length by using `pathconf'.  */
 
123
 
 
124
        if (value == 0) {
 
125
                long int x = pathconf(G_DIR_SEPARATOR_S, _PC_PATH_MAX);
 
126
 
 
127
                if (x > 0)
 
128
                        value = x;
 
129
                else
 
130
#ifdef MAXPATHLEN
 
131
                        return MAXPATHLEN;
 
132
#else
 
133
                        return 4096;
 
134
#endif
 
135
        }
 
136
 
 
137
        return value;
 
138
}
 
139
#endif
 
140
 
 
141
#ifdef HAVE_OPEN64
 
142
#define OPEN open64
 
143
#else
 
144
#define OPEN g_open
 
145
#endif
 
146
 
 
147
#if defined(HAVE_LSEEK64) && defined(HAVE_OFF64_T)
 
148
#define LSEEK lseek64
 
149
#define OFF_T off64_t
 
150
#else
 
151
#define LSEEK lseek
 
152
#define OFF_T off_t
 
153
#endif
 
154
 
 
155
#ifdef G_OS_WIN32
 
156
 
 
157
static int
 
158
ftruncate (int     fd,
 
159
           guint64 size)
 
160
{
 
161
        /* FIXME: not threadsafe at all! */
 
162
        LARGE_INTEGER origpos;
 
163
        int retval = -1;
 
164
 
 
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;
 
170
 
 
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)
 
177
                        retval = 0;
 
178
        }
 
179
 
 
180
        if (retval == -1)
 
181
                errno = EIO;
 
182
 
 
183
        return retval;
 
184
}
 
185
 
 
186
#endif
 
187
 
 
188
static gchar *
 
189
get_path_from_uri (GnomeVFSURI const *uri)
 
190
{
 
191
        gchar *path;
 
192
 
 
193
        path = gnome_vfs_unescape_string (uri->text, DIR_SEPARATORS);
 
194
                
 
195
        if (path == NULL) {
 
196
                return NULL;
 
197
        }
 
198
 
 
199
        if (!g_path_is_absolute (path)) {
 
200
                g_free (path);
 
201
                return NULL;
 
202
        }
 
203
#ifdef G_OS_WIN32
 
204
        /* Drop slash in front of drive letter */
 
205
        if (path[0] == '/' && g_ascii_isalpha (path[1]) && path[2] == ':') {
 
206
                gchar *tem = path;
 
207
                path = g_strdup (path + 1);
 
208
                g_free (tem);
 
209
        }
 
210
#endif
 
211
        return path;
 
212
}
 
213
 
 
214
static gchar *
 
215
get_base_from_uri (GnomeVFSURI const *uri)
 
216
{
 
217
        gchar *escaped_base, *base;
 
218
 
 
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);
 
222
        return base;
 
223
}
 
224
 
 
225
typedef struct {
 
226
        GnomeVFSURI *uri;
 
227
        gint fd;
 
228
} FileHandle;
 
229
 
 
230
static FileHandle *
 
231
file_handle_new (GnomeVFSURI *uri,
 
232
                 gint fd)
 
233
{
 
234
        FileHandle *result;
 
235
        result = g_new (FileHandle, 1);
 
236
 
 
237
        result->uri = gnome_vfs_uri_ref (uri);
 
238
        result->fd = fd;
 
239
 
 
240
        return result;
 
241
}
 
242
 
 
243
static void
 
244
file_handle_destroy (FileHandle *handle)
 
245
{
 
246
        gnome_vfs_uri_unref (handle->uri);
 
247
        g_free (handle);
 
248
}
 
249
 
 
250
static GnomeVFSResult
 
251
do_open (GnomeVFSMethod *method,
 
252
         GnomeVFSMethodHandle **method_handle,
 
253
         GnomeVFSURI *uri,
 
254
         GnomeVFSOpenMode mode,
 
255
         GnomeVFSContext *context)
 
256
{
 
257
        FileHandle *file_handle;
 
258
        gint fd;
 
259
        mode_t unix_mode;
 
260
        gchar *file_name;
 
261
        struct stat statbuf;
 
262
 
 
263
        _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
 
264
        _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
 
265
 
 
266
        if (mode & GNOME_VFS_OPEN_READ) {
 
267
                if (mode & GNOME_VFS_OPEN_WRITE)
 
268
                        unix_mode = O_RDWR;
 
269
                else
 
270
                        unix_mode = O_RDONLY;
 
271
        } else {
 
272
                if (mode & GNOME_VFS_OPEN_WRITE)
 
273
                        unix_mode = O_WRONLY;
 
274
                else
 
275
                        return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
 
276
        }
 
277
#ifdef G_OS_WIN32
 
278
        unix_mode |= _O_BINARY;
 
279
#endif
 
280
 
 
281
        if ((mode & GNOME_VFS_OPEN_TRUNCATE) ||
 
282
            (!(mode & GNOME_VFS_OPEN_RANDOM) && (mode & GNOME_VFS_OPEN_WRITE)))
 
283
                unix_mode |= O_TRUNC;
 
284
 
 
285
        file_name = get_path_from_uri (uri);
 
286
        if (file_name == NULL)
 
287
                return GNOME_VFS_ERROR_INVALID_URI;
 
288
 
 
289
        do
 
290
                fd = OPEN (file_name, unix_mode, 0);
 
291
        while (fd == -1
 
292
               && errno == EINTR
 
293
               && ! gnome_vfs_context_check_cancellation (context));
 
294
 
 
295
        g_free (file_name);
 
296
 
 
297
        if (fd == -1)
 
298
                return gnome_vfs_result_from_errno ();
 
299
 
 
300
#ifdef HAVE_POSIX_FADVISE
 
301
        if (! (mode & GNOME_VFS_OPEN_RANDOM)) {
 
302
                posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
 
303
        }
 
304
#endif
 
305
        
 
306
        if (fstat (fd, &statbuf) != 0)
 
307
                return gnome_vfs_result_from_errno ();
 
308
 
 
309
        if (S_ISDIR (statbuf.st_mode)) {
 
310
                close (fd);
 
311
                return GNOME_VFS_ERROR_IS_DIRECTORY;
 
312
        }
 
313
 
 
314
        file_handle = file_handle_new (uri, fd);
 
315
        
 
316
        *method_handle = (GnomeVFSMethodHandle *) file_handle;
 
317
 
 
318
        return GNOME_VFS_OK;
 
319
}
 
320
 
 
321
static GnomeVFSResult
 
322
do_create (GnomeVFSMethod *method,
 
323
           GnomeVFSMethodHandle **method_handle,
 
324
           GnomeVFSURI *uri,
 
325
           GnomeVFSOpenMode mode,
 
326
           gboolean exclusive,
 
327
           guint perm,
 
328
           GnomeVFSContext *context)
 
329
{
 
330
        FileHandle *file_handle;
 
331
        gint fd;
 
332
        mode_t unix_mode;
 
333
        gchar *file_name;
 
334
 
 
335
        _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
 
336
        _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
 
337
 
 
338
        unix_mode = O_CREAT | O_TRUNC;
 
339
        
 
340
#ifdef G_OS_WIN32
 
341
        unix_mode |= _O_BINARY;
 
342
#endif
 
343
        if (!(mode & GNOME_VFS_OPEN_WRITE))
 
344
                return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
 
345
 
 
346
        if (mode & GNOME_VFS_OPEN_READ)
 
347
                unix_mode |= O_RDWR;
 
348
        else
 
349
                unix_mode |= O_WRONLY;
 
350
 
 
351
        if (exclusive)
 
352
                unix_mode |= O_EXCL;
 
353
 
 
354
        file_name = get_path_from_uri (uri);
 
355
        if (file_name == NULL)
 
356
                return GNOME_VFS_ERROR_INVALID_URI;
 
357
 
 
358
        do
 
359
                fd = OPEN (file_name, unix_mode, perm);
 
360
        while (fd == -1
 
361
               && errno == EINTR
 
362
               && ! gnome_vfs_context_check_cancellation (context));
 
363
 
 
364
        g_free (file_name);
 
365
 
 
366
        if (fd == -1)
 
367
                return gnome_vfs_result_from_errno ();
 
368
 
 
369
        file_handle = file_handle_new (uri, fd);
 
370
 
 
371
        *method_handle = (GnomeVFSMethodHandle *) file_handle;
 
372
 
 
373
        return GNOME_VFS_OK;
 
374
}
 
375
 
 
376
static GnomeVFSResult
 
377
do_close (GnomeVFSMethod *method,
 
378
          GnomeVFSMethodHandle *method_handle,
 
379
          GnomeVFSContext *context)
 
380
{
 
381
        FileHandle *file_handle;
 
382
        gint close_retval;
 
383
 
 
384
        g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
 
385
 
 
386
        file_handle = (FileHandle *) method_handle;
 
387
 
 
388
        do
 
389
                close_retval = close (file_handle->fd);
 
390
        while (close_retval != 0
 
391
               && errno == EINTR
 
392
               && ! gnome_vfs_context_check_cancellation (context));
 
393
 
 
394
        /* FIXME bugzilla.eazel.com 1163: Should do this even after a failure?  */
 
395
        file_handle_destroy (file_handle);
 
396
 
 
397
        if (close_retval != 0) {
 
398
                return gnome_vfs_result_from_errno ();
 
399
        }
 
400
 
 
401
        return GNOME_VFS_OK;
 
402
}
 
403
 
 
404
static GnomeVFSResult
 
405
do_forget_cache (GnomeVFSMethod *method,
 
406
                 GnomeVFSMethodHandle *method_handle,
 
407
                 GnomeVFSFileOffset offset,
 
408
                 GnomeVFSFileSize size)
 
409
{
 
410
        FileHandle *file_handle;
 
411
 
 
412
        g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
 
413
 
 
414
        file_handle = (FileHandle *) method_handle;
 
415
 
 
416
#ifdef HAVE_POSIX_FADVISE
 
417
        posix_fadvise (file_handle->fd, offset, size, POSIX_FADV_DONTNEED);
 
418
#endif
 
419
        
 
420
        return GNOME_VFS_OK;
 
421
}
 
422
 
 
423
 
 
424
static GnomeVFSResult
 
425
do_read (GnomeVFSMethod *method,
 
426
         GnomeVFSMethodHandle *method_handle,
 
427
         gpointer buffer,
 
428
         GnomeVFSFileSize num_bytes,
 
429
         GnomeVFSFileSize *bytes_read,
 
430
         GnomeVFSContext *context)
 
431
{
 
432
        FileHandle *file_handle;
 
433
        gint read_val;
 
434
 
 
435
        g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
 
436
 
 
437
        file_handle = (FileHandle *) method_handle;
 
438
 
 
439
        do {
 
440
                read_val = read (file_handle->fd, buffer, num_bytes);
 
441
        } while (read_val == -1
 
442
                 && errno == EINTR
 
443
                 && ! gnome_vfs_context_check_cancellation (context));
 
444
 
 
445
        if (read_val == -1) {
 
446
                *bytes_read = 0;
 
447
                return gnome_vfs_result_from_errno ();
 
448
        } else {
 
449
                *bytes_read = read_val;
 
450
 
 
451
                /* Getting 0 from read() means EOF! */
 
452
                if (read_val == 0) {
 
453
                        return GNOME_VFS_ERROR_EOF;
 
454
                }
 
455
        }
 
456
        return GNOME_VFS_OK;
 
457
}
 
458
 
 
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)
 
466
{
 
467
        FileHandle *file_handle;
 
468
        gint write_val;
 
469
 
 
470
        g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
 
471
 
 
472
        file_handle = (FileHandle *) method_handle;
 
473
 
 
474
        do
 
475
                write_val = write (file_handle->fd, buffer, num_bytes);
 
476
        while (write_val == -1
 
477
               && errno == EINTR
 
478
               && ! gnome_vfs_context_check_cancellation (context));
 
479
 
 
480
        if (write_val == -1) {
 
481
                *bytes_written = 0;
 
482
                return gnome_vfs_result_from_errno ();
 
483
        } else {
 
484
                *bytes_written = write_val;
 
485
                return GNOME_VFS_OK;
 
486
        }
 
487
}
 
488
 
 
489
 
 
490
static gint
 
491
seek_position_to_unix (GnomeVFSSeekPosition position)
 
492
{
 
493
        switch (position) {
 
494
        case GNOME_VFS_SEEK_START:
 
495
                return SEEK_SET;
 
496
        case GNOME_VFS_SEEK_CURRENT:
 
497
                return SEEK_CUR;
 
498
        case GNOME_VFS_SEEK_END:
 
499
                return SEEK_END;
 
500
        default:
 
501
                g_warning (_("Unknown GnomeVFSSeekPosition %d"), position);
 
502
                return SEEK_SET; /* bogus */
 
503
        }
 
504
}
 
505
 
 
506
static GnomeVFSResult
 
507
do_seek (GnomeVFSMethod *method,
 
508
         GnomeVFSMethodHandle *method_handle,
 
509
         GnomeVFSSeekPosition whence,
 
510
         GnomeVFSFileOffset offset,
 
511
         GnomeVFSContext *context)
 
512
{
 
513
        FileHandle *file_handle;
 
514
        gint lseek_whence;
 
515
 
 
516
        file_handle = (FileHandle *) method_handle;
 
517
        lseek_whence = seek_position_to_unix (whence);
 
518
 
 
519
        if (LSEEK (file_handle->fd, offset, lseek_whence) == -1) {
 
520
                if (errno == ESPIPE)
 
521
                        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
522
                else
 
523
                        return gnome_vfs_result_from_errno ();
 
524
        }
 
525
 
 
526
        return GNOME_VFS_OK;
 
527
}
 
528
 
 
529
static GnomeVFSResult
 
530
do_tell (GnomeVFSMethod *method,
 
531
         GnomeVFSMethodHandle *method_handle,
 
532
         GnomeVFSFileSize *offset_return)
 
533
{
 
534
        FileHandle *file_handle;
 
535
        OFF_T offset;
 
536
 
 
537
        file_handle = (FileHandle *) method_handle;
 
538
 
 
539
        offset = LSEEK (file_handle->fd, 0, SEEK_CUR);
 
540
        if (offset == -1) {
 
541
                if (errno == ESPIPE)
 
542
                        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
543
                else
 
544
                        return gnome_vfs_result_from_errno ();
 
545
        }
 
546
 
 
547
        *offset_return = offset;
 
548
        return GNOME_VFS_OK;
 
549
}
 
550
 
 
551
 
 
552
static GnomeVFSResult
 
553
do_truncate_handle (GnomeVFSMethod *method,
 
554
                    GnomeVFSMethodHandle *method_handle,
 
555
                    GnomeVFSFileSize where,
 
556
                    GnomeVFSContext *context)
 
557
{
 
558
        FileHandle *file_handle;
 
559
 
 
560
        g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);
 
561
 
 
562
        file_handle = (FileHandle *) method_handle;
 
563
 
 
564
        if (ftruncate (file_handle->fd, where) == 0) {
 
565
                return GNOME_VFS_OK;
 
566
        } else {
 
567
                switch (errno) {
 
568
                case EBADF:
 
569
                case EROFS:
 
570
                        return GNOME_VFS_ERROR_READ_ONLY;
 
571
                case EINVAL:
 
572
                        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
573
                default:
 
574
                        return GNOME_VFS_ERROR_GENERIC;
 
575
                }
 
576
        }
 
577
}
 
578
 
 
579
static GnomeVFSResult
 
580
do_truncate (GnomeVFSMethod *method,
 
581
             GnomeVFSURI *uri,
 
582
             GnomeVFSFileSize where,
 
583
             GnomeVFSContext *context)
 
584
{
 
585
#ifndef G_OS_WIN32
 
586
        gchar *path;
 
587
 
 
588
        path = get_path_from_uri (uri);
 
589
        if (path == NULL)
 
590
                return GNOME_VFS_ERROR_INVALID_URI;
 
591
 
 
592
        if (truncate (path, where) == 0) {
 
593
                g_free (path);
 
594
                return GNOME_VFS_OK;
 
595
        } else {
 
596
                g_free (path);
 
597
                switch (errno) {
 
598
                case EBADF:
 
599
                case EROFS:
 
600
                        return GNOME_VFS_ERROR_READ_ONLY;
 
601
                case EINVAL:
 
602
                        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
603
                default:
 
604
                        return GNOME_VFS_ERROR_GENERIC;
 
605
                }
 
606
        }
 
607
#else
 
608
        g_warning ("Not implemented: file::do_truncate()");
 
609
        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
610
#endif
 
611
}
 
612
 
 
613
typedef struct {
 
614
        GnomeVFSURI *uri;
 
615
        GnomeVFSFileInfoOptions options;
 
616
#ifndef G_OS_WIN32
 
617
        DIR *dir;
 
618
        struct dirent *current_entry;
 
619
#else
 
620
        GDir *dir;
 
621
#endif
 
622
        gchar *name_buffer;
 
623
        gchar *name_ptr;
 
624
} DirectoryHandle;
 
625
 
 
626
static DirectoryHandle *
 
627
directory_handle_new (GnomeVFSURI *uri,
 
628
#ifndef G_OS_WIN32
 
629
                      DIR *dir,
 
630
#else
 
631
                      GDir *dir,
 
632
#endif
 
633
                      GnomeVFSFileInfoOptions options)
 
634
{
 
635
        DirectoryHandle *result;
 
636
        gchar *full_name;
 
637
        guint full_name_len;
 
638
 
 
639
        result = g_new (DirectoryHandle, 1);
 
640
 
 
641
        result->uri = gnome_vfs_uri_ref (uri);
 
642
        result->dir = dir;
 
643
 
 
644
#ifndef G_OS_WIN32
 
645
        /* Reserve extra space for readdir_r, see man page */
 
646
        result->current_entry = g_malloc (sizeof (struct dirent) + GET_PATH_MAX() + 1);
 
647
#endif
 
648
 
 
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);
 
652
 
 
653
        result->name_buffer = g_malloc (full_name_len + GET_PATH_MAX () + 2);
 
654
        memcpy (result->name_buffer, full_name, full_name_len);
 
655
        
 
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;
 
658
 
 
659
        result->name_ptr = result->name_buffer + full_name_len;
 
660
 
 
661
        g_free (full_name);
 
662
 
 
663
        result->options = options;
 
664
 
 
665
        return result;
 
666
}
 
667
 
 
668
static void
 
669
directory_handle_destroy (DirectoryHandle *directory_handle)
 
670
{
 
671
        gnome_vfs_uri_unref (directory_handle->uri);
 
672
        g_free (directory_handle->name_buffer);
 
673
#ifndef G_OS_WIN32
 
674
        g_free (directory_handle->current_entry);
 
675
#endif
 
676
        g_free (directory_handle);
 
677
}
 
678
 
 
679
/* MIME detection code.  */
 
680
static void
 
681
get_mime_type (GnomeVFSFileInfo *info,
 
682
               const char *full_name,
 
683
               GnomeVFSFileInfoOptions options,
 
684
               struct stat *stat_buffer)
 
685
{
 
686
        const char *mime_type;
 
687
 
 
688
        mime_type = NULL;
 
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
 
693
                 */
 
694
                mime_type = "x-special/symlink";
 
695
        } else {
 
696
                if (options & GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE) {
 
697
                        mime_type = gnome_vfs_get_file_mime_type (full_name,
 
698
                                                                  stat_buffer, TRUE);
 
699
                } else if (options & GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) {
 
700
                        mime_type = gnome_vfs_get_file_mime_type (full_name,
 
701
                                                                  stat_buffer, FALSE);
 
702
                } else {
 
703
                        mime_type = gnome_vfs_get_file_mime_type_fast (full_name,
 
704
                                                                       stat_buffer);
 
705
                }
 
706
        }
 
707
 
 
708
        g_assert (mime_type);
 
709
        info->mime_type = g_strdup (mime_type);
 
710
        info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
 
711
}
 
712
 
 
713
#ifndef G_OS_WIN32
 
714
static gchar *
 
715
read_link (const gchar *full_name)
 
716
{
 
717
        gchar *buffer;
 
718
        guint size;
 
719
 
 
720
        size = 256;
 
721
        buffer = g_malloc (size);
 
722
          
 
723
        while (1) {
 
724
                int read_size;
 
725
 
 
726
                read_size = readlink (full_name, buffer, size);
 
727
                if (read_size < 0) {
 
728
                        g_free (buffer);
 
729
                        return NULL;
 
730
                }
 
731
                if (read_size < size) {
 
732
                        buffer[read_size] = 0;
 
733
                        return buffer;
 
734
                }
 
735
                size *= 2;
 
736
                buffer = g_realloc (buffer, size);
 
737
        }
 
738
}
 
739
#endif
 
740
 
 
741
#ifdef HAVE_SELINUX
 
742
/* convert a SELinux scurity context string to a g_malloc() compatible string */
 
743
static char *sec_con2g_str(char *tmp)
 
744
{
 
745
        char *ret = tmp;
 
746
  
 
747
        if (tmp) {
 
748
                 ret = g_strdup(tmp);
 
749
                 freecon(tmp);
 
750
        }
 
751
 
 
752
        return ret;
 
753
}
 
754
#endif
 
755
 
 
756
/* Get the SELinux security context */
 
757
static int
 
758
get_selinux_context (
 
759
        GnomeVFSFileInfo *info,
 
760
        const char *full_name,
 
761
        GnomeVFSFileInfoOptions options)
 
762
{
 
763
#ifdef HAVE_SELINUX
 
764
        if (is_selinux_enabled()) {
 
765
          
 
766
                if ((options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS) == 0
 
767
                        && (info->type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK)) {
 
768
 
 
769
                        /* we are a symlink and aren't asked to follow -
 
770
                         * return the type for a symlink */
 
771
                                                
 
772
                        if (lgetfilecon_raw(full_name, &info->selinux_context) < 0)
 
773
                                return gnome_vfs_result_from_errno ();
 
774
                } else {
 
775
 
 
776
                        if (getfilecon_raw(full_name, &info->selinux_context) < 0)
 
777
                                return gnome_vfs_result_from_errno ();
 
778
                }
 
779
 
 
780
                info->selinux_context = sec_con2g_str(info->selinux_context);
 
781
                
 
782
                info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SELINUX_CONTEXT;
 
783
                return GNOME_VFS_OK;
 
784
        }
 
785
#endif
 
786
        return GNOME_VFS_OK;
 
787
}
 
788
 
 
789
/* Get the SELinux security context from handle */
 
790
static int 
 
791
get_selinux_context_from_handle (
 
792
        GnomeVFSFileInfo *info,
 
793
        FileHandle *handle)
 
794
{
 
795
#ifdef HAVE_SELINUX
 
796
        if (is_selinux_enabled()) {
 
797
                if (fgetfilecon_raw(handle->fd, &info->selinux_context) >= 0) 
 
798
                        return gnome_vfs_result_from_errno ();  
 
799
 
 
800
                info->selinux_context = sec_con2g_str(info->selinux_context);
 
801
                
 
802
                info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SELINUX_CONTEXT;
 
803
                return GNOME_VFS_OK;
 
804
        }       
 
805
#endif
 
806
        return GNOME_VFS_OK;
 
807
}
 
808
 
 
809
/* Set the SELinux security context */
 
810
static int 
 
811
set_selinux_context (
 
812
        const GnomeVFSFileInfo *info,
 
813
        const char *full_name) 
 
814
{
 
815
#ifdef HAVE_SELINUX
 
816
        if (is_selinux_enabled()) {
 
817
                if (setfilecon_raw(full_name, info->selinux_context) < 0)
 
818
                        return gnome_vfs_result_from_errno ();
 
819
 
 
820
                return GNOME_VFS_OK;
 
821
        }
 
822
#endif
 
823
        return GNOME_VFS_OK;
 
824
}
 
825
 
 
826
static void
 
827
get_access_info (GnomeVFSFileInfo *file_info,
 
828
                 const gchar *full_name)
 
829
{
 
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
 
833
      */
 
834
#ifdef G_OS_WIN32
 
835
        if (g_access (full_name, R_OK) == 0) {
 
836
                file_info->permissions |= GNOME_VFS_PERM_ACCESS_READABLE;
 
837
        }
 
838
        if (g_access (full_name, W_OK) == 0) {
 
839
                file_info->permissions |= GNOME_VFS_PERM_ACCESS_WRITABLE;
 
840
        }
 
841
        if (g_file_test (full_name, G_FILE_TEST_IS_EXECUTABLE)) {
 
842
                file_info->permissions |= GNOME_VFS_PERM_ACCESS_EXECUTABLE;
 
843
        }
 
844
#else
 
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
 
847
         * calls needed
 
848
         */
 
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;
 
853
                }
 
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;
 
856
        } else {
 
857
                if (g_access (full_name, R_OK) == 0) {
 
858
                        file_info->permissions |= GNOME_VFS_PERM_ACCESS_READABLE;
 
859
                } else {
 
860
                        if (g_access (full_name, W_OK) == 0) {
 
861
                                file_info->permissions |= GNOME_VFS_PERM_ACCESS_WRITABLE;
 
862
                        }
 
863
                        if (g_access (full_name, X_OK) == 0) {
 
864
                                file_info->permissions |= GNOME_VFS_PERM_ACCESS_EXECUTABLE;
 
865
                        }
 
866
                }
 
867
        }
 
868
#endif
 
869
 
 
870
     file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_ACCESS;
 
871
}
 
872
 
 
873
static GnomeVFSResult
 
874
get_stat_info (GnomeVFSFileInfo *file_info,
 
875
               const gchar *full_name,
 
876
               GnomeVFSFileInfoOptions options,
 
877
               struct stat *statptr)
 
878
{
 
879
        struct stat statbuf;
 
880
#ifndef G_OS_WIN32
 
881
        gboolean is_symlink;
 
882
        char *link_file_path;
 
883
        char *symlink_name;
 
884
        char *symlink_dir;
 
885
        char *newpath;
 
886
#endif
 
887
        gboolean recursive;
 
888
        
 
889
        recursive = FALSE;
 
890
 
 
891
        GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
 
892
 
 
893
        if (statptr == NULL) {
 
894
                statptr = &statbuf;
 
895
        }
 
896
 
 
897
        if (g_lstat (full_name, statptr) != 0) {
 
898
                return gnome_vfs_result_from_errno ();
 
899
        }
 
900
 
 
901
 
 
902
#ifndef G_OS_WIN32
 
903
        is_symlink = S_ISLNK (statptr->st_mode);
 
904
 
 
905
        if ((options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS) && is_symlink) {
 
906
                if (g_stat (full_name, statptr) != 0) {
 
907
                        if (errno == ELOOP) {
 
908
                                recursive = TRUE;
 
909
                        }
 
910
 
 
911
                        /* It's a broken symlink, revert to the lstat. This is sub-optimal but
 
912
                         * acceptable because it's not a common case.
 
913
                         */
 
914
                        if (g_lstat (full_name, statptr) != 0) {
 
915
                                return gnome_vfs_result_from_errno ();
 
916
                        }
 
917
                }
 
918
                GNOME_VFS_FILE_INFO_SET_SYMLINK (file_info, TRUE);
 
919
        }
 
920
#endif
 
921
        gnome_vfs_stat_to_file_info (file_info, statptr);
 
922
 
 
923
#ifndef G_OS_WIN32
 
924
        if (is_symlink) {
 
925
                symlink_name = NULL;
 
926
                link_file_path = g_strdup (full_name);
 
927
                
 
928
                /* We will either successfully read the link name or return
 
929
                 * NULL if read_link fails -- flag it as a valid field either
 
930
                 * way.
 
931
                 */
 
932
                file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SYMLINK_NAME;
 
933
 
 
934
                while (TRUE) {                  
 
935
                        /* Deal with multiple-level symlinks by following them as
 
936
                         * far as we can.
 
937
                         */
 
938
 
 
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 ();
 
944
                        }
 
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,
 
949
                                                            symlink_name, NULL);
 
950
                                g_free (symlink_dir);
 
951
                                g_free (symlink_name);
 
952
                                symlink_name = gnome_vfs_make_path_name_canonical (newpath);
 
953
                                g_free (newpath);
 
954
                        }
 
955
                        
 
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 */
 
958
                                || recursive
 
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 */
 
964
                                break;
 
965
                        }
 
966
                        g_free (link_file_path);
 
967
                        link_file_path = g_strdup (symlink_name);
 
968
                }
 
969
                g_free (link_file_path);
 
970
 
 
971
                file_info->symlink_name = symlink_name;
 
972
        }
 
973
#endif
 
974
        return GNOME_VFS_OK;
 
975
}
 
976
 
 
977
static GnomeVFSResult
 
978
get_stat_info_from_handle (GnomeVFSFileInfo *file_info,
 
979
                           FileHandle *handle,
 
980
                           GnomeVFSFileInfoOptions options,
 
981
                           struct stat *statptr)
 
982
{
 
983
        struct stat statbuf;
 
984
 
 
985
        if (statptr == NULL) {
 
986
                statptr = &statbuf;
 
987
        }
 
988
 
 
989
        if (fstat (handle->fd, statptr) != 0) {
 
990
                return gnome_vfs_result_from_errno ();
 
991
        }
 
992
        
 
993
        gnome_vfs_stat_to_file_info (file_info, statptr);
 
994
        GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
 
995
 
 
996
        return GNOME_VFS_OK;
 
997
}
 
998
 
 
999
 
 
1000
static GnomeVFSResult
 
1001
do_open_directory (GnomeVFSMethod *method,
 
1002
                   GnomeVFSMethodHandle **method_handle,
 
1003
                   GnomeVFSURI *uri,
 
1004
                   GnomeVFSFileInfoOptions options,
 
1005
                   GnomeVFSContext *context)
 
1006
{
 
1007
        gchar *directory_name;
 
1008
#ifndef G_OS_WIN32
 
1009
        DIR *dir;
 
1010
#else
 
1011
        GDir *dir;
 
1012
#endif
 
1013
 
 
1014
        directory_name = get_path_from_uri (uri);
 
1015
        if (directory_name == NULL)
 
1016
                return GNOME_VFS_ERROR_INVALID_URI;
 
1017
 
 
1018
#ifndef G_OS_WIN32
 
1019
        dir = opendir (directory_name);
 
1020
#else
 
1021
        dir = g_dir_open (directory_name, 0, NULL);
 
1022
#endif
 
1023
        g_free (directory_name);
 
1024
        if (dir == NULL)
 
1025
                return gnome_vfs_result_from_errno ();
 
1026
 
 
1027
        *method_handle
 
1028
                = (GnomeVFSMethodHandle *) directory_handle_new (uri, dir,
 
1029
                                                                 options);
 
1030
 
 
1031
        return GNOME_VFS_OK;
 
1032
}
 
1033
 
 
1034
static GnomeVFSResult
 
1035
do_close_directory (GnomeVFSMethod *method,
 
1036
                    GnomeVFSMethodHandle *method_handle,
 
1037
                    GnomeVFSContext *context)
 
1038
{
 
1039
        DirectoryHandle *directory_handle;
 
1040
 
 
1041
        directory_handle = (DirectoryHandle *) method_handle;
 
1042
 
 
1043
#ifndef G_OS_WIN32
 
1044
        closedir (directory_handle->dir);
 
1045
#else
 
1046
        g_dir_close (directory_handle->dir);
 
1047
#endif
 
1048
 
 
1049
        directory_handle_destroy (directory_handle);
 
1050
 
 
1051
        return GNOME_VFS_OK;
 
1052
}
 
1053
 
 
1054
#ifndef HAVE_READDIR_R
 
1055
G_LOCK_DEFINE_STATIC (readdir);
 
1056
#endif
 
1057
 
 
1058
static GnomeVFSResult
 
1059
do_read_directory (GnomeVFSMethod *method,
 
1060
                   GnomeVFSMethodHandle *method_handle,
 
1061
                   GnomeVFSFileInfo *file_info,
 
1062
                   GnomeVFSContext *context)
 
1063
{
 
1064
#ifndef G_OS_WIN32
 
1065
        struct dirent *result;
 
1066
#else
 
1067
        const gchar *result;
 
1068
#endif
 
1069
        struct stat statbuf;
 
1070
        gchar *full_name;
 
1071
        DirectoryHandle *handle;
 
1072
 
 
1073
        handle = (DirectoryHandle *) method_handle;
 
1074
        
 
1075
        errno = 0;
 
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.
 
1080
                 */
 
1081
                if (errno == 0) {
 
1082
                        return GNOME_VFS_ERROR_EOF;
 
1083
                }
 
1084
                return gnome_vfs_result_from_errno ();
 
1085
        }
 
1086
#else
 
1087
        G_LOCK (readdir);
 
1088
        errno = 0;
 
1089
#ifndef G_OS_WIN32
 
1090
        result = readdir (handle->dir);
 
1091
#else
 
1092
        result = g_dir_read_name (handle->dir);
 
1093
#endif
 
1094
 
 
1095
        if (result == NULL && errno != 0) {
 
1096
                GnomeVFSResult ret = gnome_vfs_result_from_errno ();
 
1097
                G_UNLOCK (readdir);
 
1098
                return ret;
 
1099
        }
 
1100
#ifndef G_OS_WIN32
 
1101
        if (result != NULL) {
 
1102
                memcpy (handle->current_entry, result, sizeof (struct dirent));
 
1103
        }
 
1104
#endif
 
1105
        G_UNLOCK (readdir);
 
1106
#endif
 
1107
        
 
1108
        if (result == NULL) {
 
1109
                return GNOME_VFS_ERROR_EOF;
 
1110
        }
 
1111
 
 
1112
#ifndef G_OS_WIN32
 
1113
        file_info->name = g_strdup (result->d_name);
 
1114
        strcpy (handle->name_ptr, result->d_name);
 
1115
#else
 
1116
        file_info->name = g_strdup (result);
 
1117
        strcpy (handle->name_ptr, result);
 
1118
#endif
 
1119
        full_name = handle->name_buffer;
 
1120
 
 
1121
        if (handle->options & GNOME_VFS_FILE_INFO_NAME_ONLY) {
 
1122
                return GNOME_VFS_OK;
 
1123
        }
 
1124
 
 
1125
        if (handle->options & GNOME_VFS_FILE_INFO_GET_SELINUX_CONTEXT) {
 
1126
 
 
1127
                /* Attempt to get selinux contet, ignore error (see below) */
 
1128
                get_selinux_context(file_info, full_name, handle->options);
 
1129
        }
 
1130
                
 
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
 
1134
                 * stat info.
 
1135
                 */
 
1136
                return GNOME_VFS_OK;
 
1137
        }
 
1138
 
 
1139
        if (handle->options & GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS) {
 
1140
                get_access_info (file_info, full_name);
 
1141
        }
 
1142
        
 
1143
        if (handle->options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE) {
 
1144
                get_mime_type (file_info, full_name, handle->options, &statbuf);
 
1145
        }
 
1146
 
 
1147
        if (handle->options & GNOME_VFS_FILE_INFO_GET_ACL) {
 
1148
                file_get_acl (full_name, file_info, &statbuf, context);
 
1149
        }
 
1150
 
 
1151
        return GNOME_VFS_OK;
 
1152
}
 
1153
 
 
1154
static GnomeVFSResult
 
1155
do_get_file_info (GnomeVFSMethod *method,
 
1156
                  GnomeVFSURI *uri,
 
1157
                  GnomeVFSFileInfo *file_info,
 
1158
                  GnomeVFSFileInfoOptions options,
 
1159
                  GnomeVFSContext *context)
 
1160
{
 
1161
        GnomeVFSResult result;
 
1162
        gchar *full_name;
 
1163
        struct stat statbuf;
 
1164
 
 
1165
        full_name = get_path_from_uri (uri);
 
1166
        if (full_name == NULL)
 
1167
                return GNOME_VFS_ERROR_INVALID_URI;
 
1168
 
 
1169
        file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
 
1170
 
 
1171
        file_info->name = get_base_from_uri (uri);
 
1172
        g_assert (file_info->name != NULL);
 
1173
 
 
1174
        result = get_stat_info (file_info, full_name, options, &statbuf);
 
1175
        if (result != GNOME_VFS_OK) {
 
1176
                g_free (full_name);
 
1177
                return result;
 
1178
        }
 
1179
 
 
1180
        if (options & GNOME_VFS_FILE_INFO_GET_SELINUX_CONTEXT) {
 
1181
                get_selinux_context (file_info, full_name, options);
 
1182
        } 
 
1183
 
 
1184
        if (options & GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS) {
 
1185
                get_access_info (file_info, full_name);
 
1186
        }
 
1187
 
 
1188
        if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE) {
 
1189
                get_mime_type (file_info, full_name, options, &statbuf);
 
1190
        }
 
1191
        
 
1192
        if (options & GNOME_VFS_FILE_INFO_GET_ACL) {
 
1193
                file_get_acl (full_name, file_info, &statbuf, context); 
 
1194
        }
 
1195
 
 
1196
        g_free (full_name);
 
1197
 
 
1198
        return GNOME_VFS_OK;
 
1199
}
 
1200
 
 
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)
 
1207
{
 
1208
        FileHandle *file_handle;
 
1209
        gchar *full_name;
 
1210
        struct stat statbuf;
 
1211
        GnomeVFSResult result;
 
1212
 
 
1213
        file_handle = (FileHandle *) method_handle;
 
1214
 
 
1215
        file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
 
1216
 
 
1217
        full_name = get_path_from_uri (file_handle->uri);
 
1218
        if (full_name == NULL) {
 
1219
                return GNOME_VFS_ERROR_INVALID_URI;
 
1220
        }
 
1221
 
 
1222
        file_info->name = get_base_from_uri (file_handle->uri);
 
1223
        g_assert (file_info->name != NULL);
 
1224
 
 
1225
        result = get_stat_info_from_handle (file_info, file_handle,
 
1226
                                            options, &statbuf);
 
1227
        if (result != GNOME_VFS_OK) {
 
1228
                g_free (full_name);
 
1229
                return result;
 
1230
        }
 
1231
 
 
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) {
 
1235
                        g_free (full_name);
 
1236
                        return result;
 
1237
                }
 
1238
        }
 
1239
 
 
1240
        if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE) {
 
1241
                get_mime_type (file_info, full_name, options, &statbuf);
 
1242
        }
 
1243
 
 
1244
        if (options & GNOME_VFS_FILE_INFO_GET_ACL) {
 
1245
                file_get_acl (full_name, file_info, &statbuf, context); 
 
1246
        }
 
1247
 
 
1248
        g_free (full_name);
 
1249
 
 
1250
        return GNOME_VFS_OK;
 
1251
}
 
1252
 
 
1253
extern char *filesystem_type (char *path, char *relpath, struct stat *statp);
 
1254
 
 
1255
G_LOCK_DEFINE_STATIC (fstype);
 
1256
 
 
1257
static gboolean
 
1258
do_is_local (GnomeVFSMethod *method,
 
1259
             const GnomeVFSURI *uri)
 
1260
{
 
1261
        struct stat statbuf;
 
1262
        gboolean is_local;
 
1263
        gchar *path;
 
1264
        char *type;
 
1265
 
 
1266
        g_return_val_if_fail (uri != NULL, FALSE);
 
1267
 
 
1268
        path = get_path_from_uri (uri);
 
1269
        if (path == NULL)
 
1270
                return TRUE; /* GNOME_VFS_ERROR_INVALID_URI */
 
1271
 
 
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);
 
1275
                g_free (path);
 
1276
                path = tmp_path;
 
1277
        }
 
1278
                
 
1279
        G_LOCK (fstype);
 
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));
 
1287
        G_UNLOCK (fstype);
 
1288
 
 
1289
        g_free (path);
 
1290
        return is_local;
 
1291
}
 
1292
 
 
1293
 
 
1294
static GnomeVFSResult
 
1295
do_make_directory (GnomeVFSMethod *method,
 
1296
                   GnomeVFSURI *uri,
 
1297
                   guint perm,
 
1298
                   GnomeVFSContext *context)
 
1299
{
 
1300
        gint retval;
 
1301
        gchar *full_name;
 
1302
 
 
1303
        full_name = get_path_from_uri (uri);
 
1304
        if (full_name == NULL)
 
1305
                return GNOME_VFS_ERROR_INVALID_URI;
 
1306
 
 
1307
        retval = g_mkdir (full_name, perm);
 
1308
 
 
1309
        g_free (full_name);
 
1310
 
 
1311
        if (retval != 0) {
 
1312
                return gnome_vfs_result_from_errno ();
 
1313
        }
 
1314
 
 
1315
        return GNOME_VFS_OK;
 
1316
}
 
1317
 
 
1318
static GnomeVFSResult
 
1319
do_remove_directory (GnomeVFSMethod *method,
 
1320
                     GnomeVFSURI *uri,
 
1321
                     GnomeVFSContext *context)
 
1322
{
 
1323
        gchar *full_name;
 
1324
        gint retval;
 
1325
 
 
1326
        full_name = get_path_from_uri (uri);
 
1327
        if (full_name == NULL)
 
1328
                return GNOME_VFS_ERROR_INVALID_URI;
 
1329
 
 
1330
        retval = g_rmdir (full_name);
 
1331
 
 
1332
        g_free (full_name);
 
1333
 
 
1334
        if (retval != 0) {
 
1335
                return gnome_vfs_result_from_errno ();
 
1336
        }
 
1337
 
 
1338
        return GNOME_VFS_OK;
 
1339
}
 
1340
 
 
1341
#undef DEBUG_FIND_DIRECTORY
 
1342
/* Get rid of debugging code once we know the logic works. */
 
1343
 
 
1344
#define TRASH_DIRECTORY_NAME_BASE ".Trash"
 
1345
#define MAX_TRASH_SEARCH_DEPTH 5
 
1346
 
 
1347
/* mkdir_recursive 
 
1348
 * Works like mkdir, except it creates all the levels of directories in @path.
 
1349
 */
 
1350
static int
 
1351
mkdir_recursive (const char *path, int permission_bits)
 
1352
{
 
1353
        struct stat stat_buffer;
 
1354
        const char *dir_separator_scanner;
 
1355
        char *current_path;
 
1356
 
 
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) {
 
1362
                                break;
 
1363
                        }       
 
1364
                        if (G_IS_DIR_SEPARATOR (*dir_separator_scanner)) {
 
1365
                                break;
 
1366
                        }
 
1367
                }
 
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;
 
1373
                                 * bail
 
1374
                                 */
 
1375
                                g_free (current_path);
 
1376
                                return -1;
 
1377
                        }
 
1378
                        g_free (current_path);
 
1379
                }
 
1380
                if (!*dir_separator_scanner) {
 
1381
                        break;
 
1382
                }       
 
1383
        }
 
1384
        return 0;
 
1385
}
 
1386
 
 
1387
 
 
1388
static char *
 
1389
append_to_path (const char *path, const char *name)
 
1390
{
 
1391
        return g_build_filename (path, name, NULL);
 
1392
}
 
1393
 
 
1394
static char *
 
1395
append_trash_path (const char *path)
 
1396
{       
 
1397
        char *per_user_part; 
 
1398
        char *retval;
 
1399
        
 
1400
        per_user_part = g_strdup_printf ("%s-%lu",
 
1401
                                         TRASH_DIRECTORY_NAME_BASE,
 
1402
                                         (unsigned long) geteuid());
 
1403
 
 
1404
        retval = g_build_filename (path, per_user_part, "files", NULL);
 
1405
        
 
1406
        g_free (per_user_part);
 
1407
 
 
1408
        return retval;
 
1409
}
 
1410
 
 
1411
static char *
 
1412
find_trash_in_hierarchy (const char *start_dir, dev_t near_device_id, GnomeVFSContext *context)
 
1413
{
 
1414
        char *trash_path;
 
1415
        struct stat stat_buffer;
 
1416
 
 
1417
        if (gnome_vfs_context_check_cancellation (context))
 
1418
                return NULL;
 
1419
 
 
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);
 
1425
                return trash_path;
 
1426
        }
 
1427
        g_free (trash_path);
 
1428
 
 
1429
        return NULL;
 
1430
}
 
1431
 
 
1432
static GList *cached_trash_directories;
 
1433
G_LOCK_DEFINE_STATIC (cached_trash_directories);
 
1434
 
 
1435
/* Element used to store chached Trash entries in the local, in-memory Trash item cache. */
 
1436
typedef struct {
 
1437
        char *path;
 
1438
        char *device_mount_point;
 
1439
        dev_t device_id;
 
1440
} TrashDirectoryCachedItem;
 
1441
 
 
1442
typedef struct {
 
1443
        dev_t device_id;
 
1444
} FindByDeviceIDParameters;
 
1445
 
 
1446
static int
 
1447
match_trash_item_by_device_id (gconstpointer item, gconstpointer data)
 
1448
{
 
1449
        const TrashDirectoryCachedItem *cached_item;
 
1450
        FindByDeviceIDParameters *parameters;
 
1451
 
 
1452
        cached_item = (const TrashDirectoryCachedItem *)item;
 
1453
        parameters = (FindByDeviceIDParameters *)data;
 
1454
        
 
1455
        return cached_item->device_id == parameters->device_id ? 0 : -1;
 
1456
}
 
1457
 
 
1458
static char *
 
1459
try_creating_trash_in (const char *path, guint permissions)
 
1460
{
 
1461
        char *trash_path;
 
1462
 
 
1463
 
 
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);
 
1468
#endif
 
1469
                return trash_path;
 
1470
        }
 
1471
 
 
1472
#ifdef DEBUG_FIND_DIRECTORY
 
1473
        g_print ("failed to create trash in %s\n", trash_path);
 
1474
#endif
 
1475
        g_free (trash_path);
 
1476
        return NULL;
 
1477
}
 
1478
 
 
1479
static char *
 
1480
find_disk_top_directory (const char *item_on_disk,
 
1481
                         dev_t near_device_id,
 
1482
                         GnomeVFSContext *context)
 
1483
{
 
1484
        char *disk_top_directory;
 
1485
        struct stat stat_buffer;
 
1486
 
 
1487
        disk_top_directory = g_strdup (item_on_disk);
 
1488
 
 
1489
        /* Walk up in the hierarchy, finding the top-most point that still
 
1490
         * matches our device ID -- the root directory of the volume.
 
1491
         */
 
1492
        for (;;) {
 
1493
                char *previous_search_directory;
 
1494
                char *last_slash;
 
1495
                
 
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);
 
1500
                        break;
 
1501
                }
 
1502
                
 
1503
                *last_slash = '\0';
 
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;
 
1509
                        break;
 
1510
                }
 
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.  
 
1515
                 */
 
1516
                if (gnome_vfs_context_check_cancellation (context)) {
 
1517
                        g_free (previous_search_directory);
 
1518
                        g_free (disk_top_directory);
 
1519
                        return NULL;
 
1520
                }
 
1521
        }
 
1522
        return disk_top_directory;
 
1523
}
 
1524
 
 
1525
#define TRASH_ENTRY_CACHE_PARENT ".gnome/gnome-vfs"
 
1526
#define TRASH_ENTRY_CACHE_NAME ".trash_entry_cache"
 
1527
#define NON_EXISTENT_TRASH_ENTRY "-"
 
1528
 
 
1529
/* Save the localy cached Trashed paths on disk in the user's home
 
1530
 * directory.
 
1531
 */
 
1532
static void
 
1533
save_trash_entry_cache (void)
 
1534
{
 
1535
        int cache_file;
 
1536
        char *cache_file_parent, *cache_file_path;
 
1537
        GList *p;
 
1538
        char *buffer, *escaped_path, *escaped_mount_point;
 
1539
 
 
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);
 
1542
 
 
1543
        if (mkdir_recursive (cache_file_parent, 0777) != 0) {
 
1544
                g_warning ("failed to create trash item cache file");
 
1545
                return;
 
1546
        }
 
1547
 
 
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");
 
1551
                return;
 
1552
        }
 
1553
 
 
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);
 
1560
                        
 
1561
                buffer = g_strdup_printf ("%s %s\n", escaped_mount_point, escaped_path);
 
1562
                write (cache_file, buffer, strlen (buffer));
 
1563
 
 
1564
#ifdef DEBUG_FIND_DIRECTORY
 
1565
        g_print ("saving trash item cache %s\n", buffer);
 
1566
#endif
 
1567
 
 
1568
                g_free (buffer);
 
1569
                g_free (escaped_mount_point);
 
1570
                g_free (escaped_path);
 
1571
        }
 
1572
        close (cache_file);
 
1573
        
 
1574
        g_free (cache_file_path);
 
1575
        g_free (cache_file_parent);
 
1576
}
 
1577
 
 
1578
typedef struct {
 
1579
        const char *mount_point;
 
1580
        const char *trash_path;
 
1581
        dev_t device_id;
 
1582
        gboolean done;
 
1583
} UpdateOneCachedEntryContext;
 
1584
 
 
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.
 
1587
 */
 
1588
static void
 
1589
update_one_cached_trash_entry (gpointer element, gpointer cast_to_context)
 
1590
{
 
1591
        UpdateOneCachedEntryContext *context;
 
1592
        TrashDirectoryCachedItem *item;
 
1593
 
 
1594
        context = (UpdateOneCachedEntryContext *)cast_to_context;
 
1595
        item = (TrashDirectoryCachedItem *)element;
 
1596
 
 
1597
        if (context->done) {
 
1598
                /* We already took care of business in a previous iteration. */
 
1599
                return;
 
1600
        }
 
1601
 
 
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;
 
1607
 
 
1608
                /* no more work */
 
1609
                context->done = TRUE;
 
1610
        }
 
1611
}
 
1612
 
 
1613
static void
 
1614
add_local_cached_trash_entry (dev_t near_device_id, const char *trash_path, const char *mount_point)
 
1615
{
 
1616
        TrashDirectoryCachedItem *new_cached_item;
 
1617
        UpdateOneCachedEntryContext update_context;
 
1618
 
 
1619
        /* First check if we already have an entry for this mountpoint,
 
1620
         * if so, update it.
 
1621
         */
 
1622
 
 
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;
 
1627
 
 
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. */
 
1631
                return;
 
1632
        }
 
1633
        
 
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;
 
1639
 
 
1640
 
 
1641
        cached_trash_directories = g_list_prepend (cached_trash_directories, new_cached_item);
 
1642
}
 
1643
 
 
1644
static void
 
1645
add_cached_trash_entry (dev_t near_device_id, const char *trash_path, const char *mount_point)
 
1646
{
 
1647
        add_local_cached_trash_entry (near_device_id, trash_path, mount_point);
 
1648
        /* write out the local cache */
 
1649
        save_trash_entry_cache ();
 
1650
}
 
1651
 
 
1652
static void
 
1653
destroy_cached_trash_entry (TrashDirectoryCachedItem *entry)
 
1654
{
 
1655
        g_free (entry->path);
 
1656
        g_free (entry->device_mount_point);
 
1657
        g_free (entry);
 
1658
}
 
1659
 
 
1660
/* Read the cached entries for the file cache into the local Trash item cache. */
 
1661
static void
 
1662
read_saved_cached_trash_entries (void)
 
1663
{
 
1664
        char *cache_file_path;
 
1665
        FILE *cache_file;
 
1666
        char buffer[2048];
 
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;
 
1671
 
 
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;
 
1677
 
 
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,
 
1682
                                            NULL);
 
1683
        cache_file = g_fopen (cache_file_path, "r");
 
1684
 
 
1685
        if (cache_file != NULL) {
 
1686
                removed_item = FALSE;
 
1687
                for (;;) {
 
1688
                        if (fgets (buffer, sizeof (buffer), cache_file) == NULL) {
 
1689
                                break;
 
1690
                        }
 
1691
 
 
1692
                        mount_point = NULL;
 
1693
                        trash_path = 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); 
 
1698
 
 
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.
 
1708
                                         */
 
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);
 
1712
#endif
 
1713
                                } else {
 
1714
                                        removed_item = TRUE;
 
1715
                                }
 
1716
                        }
 
1717
                        
 
1718
                        g_free (trash_path);
 
1719
                        g_free (mount_point);
 
1720
                }
 
1721
                fclose (cache_file);
 
1722
                /* Save cache to get rid of stuff from on-disk cache */
 
1723
                if (removed_item) {
 
1724
                        save_trash_entry_cache ();
 
1725
                }
 
1726
        }
 
1727
        
 
1728
        g_free (cache_file_path);
 
1729
}
 
1730
 
 
1731
/* Create a Trash directory on the same disk as @full_name_near. */
 
1732
static char *
 
1733
create_trash_near (const char *full_name_near, dev_t near_device_id, const char *disk_top_directory,
 
1734
        guint permissions, GnomeVFSContext *context)
 
1735
{
 
1736
        return try_creating_trash_in (disk_top_directory, permissions);
 
1737
}
 
1738
 
 
1739
 
 
1740
static gboolean
 
1741
cached_trash_entry_exists (const TrashDirectoryCachedItem *entry)
 
1742
{
 
1743
        struct stat stat_buffer;
 
1744
        return g_lstat (entry->path, &stat_buffer) == 0;
 
1745
}
 
1746
 
 
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.
 
1749
 */
 
1750
static char *
 
1751
find_locally_cached_trash_entry_for_device_id (dev_t device_id, gboolean check_disk)
 
1752
{
 
1753
        GList *matching_item;
 
1754
        FindByDeviceIDParameters tmp;
 
1755
        const char *trash_path;
 
1756
 
 
1757
        tmp.device_id = device_id;
 
1758
 
 
1759
        matching_item = g_list_find_custom (cached_trash_directories, 
 
1760
                &tmp, match_trash_item_by_device_id);
 
1761
 
 
1762
        if (matching_item == NULL) {
 
1763
                return NULL;
 
1764
        }
 
1765
 
 
1766
        trash_path = ((TrashDirectoryCachedItem *)matching_item->data)->path;
 
1767
 
 
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);
 
1772
#endif
 
1773
                return g_strdup (NON_EXISTENT_TRASH_ENTRY);
 
1774
        }
 
1775
 
 
1776
        if (check_disk) {
 
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
 
1782
                         */
 
1783
#ifdef DEBUG_FIND_DIRECTORY
 
1784
                        g_print ("entry %s doesn't exist, removing \n", 
 
1785
                                ((TrashDirectoryCachedItem *)matching_item->data)->path);
 
1786
#endif
 
1787
                        destroy_cached_trash_entry ((TrashDirectoryCachedItem *)matching_item->data);
 
1788
                        cached_trash_directories = g_list_remove (cached_trash_directories, 
 
1789
                                matching_item->data);
 
1790
                        return NULL;
 
1791
                }
 
1792
        }
 
1793
 
 
1794
#ifdef DEBUG_FIND_DIRECTORY
 
1795
        g_print ("local cache found %s \n", trash_path);
 
1796
#endif
 
1797
        g_assert (matching_item != NULL);
 
1798
        return g_strdup (trash_path);
 
1799
}
 
1800
 
 
1801
/* Look for an entry in the file and local caches. */
 
1802
static char *
 
1803
find_cached_trash_entry_for_device (dev_t device_id, gboolean check_disk)
 
1804
{
 
1805
        if (cached_trash_directories == NULL) {
 
1806
                if (!check_disk) {
 
1807
                        return NULL;
 
1808
                }
 
1809
                read_saved_cached_trash_entries ();
 
1810
        }
 
1811
        return find_locally_cached_trash_entry_for_device_id (device_id, check_disk);
 
1812
}
 
1813
 
 
1814
/* Search for a Trash entry or create one. Called when there is no cached entry. */
 
1815
static char *
 
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)
 
1819
{
 
1820
        char *result;
 
1821
        char *disk_top_directory;
 
1822
 
 
1823
        result = NULL;
 
1824
        /* figure out the topmost disk directory */
 
1825
        disk_top_directory = find_disk_top_directory (full_name_near, 
 
1826
                                                      near_device_id, context);
 
1827
 
 
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.
 
1831
                 */
 
1832
#ifdef DEBUG_FIND_DIRECTORY
 
1833
                g_print ("failed to find top disk directory for %s\n", full_name_near);
 
1834
#endif
 
1835
                add_cached_trash_entry (near_device_id, NON_EXISTENT_TRASH_ENTRY, disk_top_directory);
 
1836
                return NULL;
 
1837
        }
 
1838
 
 
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.
 
1845
                         */
 
1846
                        result = g_strdup(NON_EXISTENT_TRASH_ENTRY);
 
1847
                }
 
1848
        }
 
1849
 
 
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);
 
1854
        }
 
1855
 
 
1856
        if (result != NULL) {
 
1857
                /* remember whatever we found for next time */
 
1858
                add_cached_trash_entry (near_device_id, result, disk_top_directory);
 
1859
        }
 
1860
 
 
1861
        g_free (disk_top_directory);
 
1862
 
 
1863
        return result;
 
1864
}
 
1865
 
 
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.
 
1868
 *
 
1869
 *     This is the only entry point for the trash cache code,
 
1870
 * we holds the lock while operating on it only here.
 
1871
 */
 
1872
static char *
 
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)
 
1876
{
 
1877
        char *result;
 
1878
 
 
1879
        G_LOCK (cached_trash_directories);
 
1880
 
 
1881
        /* look in the saved trash locations first */
 
1882
        result = find_cached_trash_entry_for_device (near_device_id, find_if_needed);
 
1883
 
 
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. 
 
1891
                         */
 
1892
#ifdef DEBUG_FIND_DIRECTORY
 
1893
                        g_print ("cache indicates no trash for %s, force a creation \n", full_name_near);
 
1894
#endif
 
1895
                        g_free (result);
 
1896
                        result = NULL;
 
1897
                }
 
1898
 
 
1899
                if (result == NULL) {
 
1900
                        /* No luck sofar. Look for the Trash on the disk, optionally create it
 
1901
                         * if we find nothing.
 
1902
                         */
 
1903
                        result = find_or_create_trash_near (full_name_near, near_device_id, 
 
1904
                                create_if_needed, find_if_needed, permissions, context);
 
1905
                }
 
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);
 
1910
                }
 
1911
        }
 
1912
        
 
1913
        if (result != NULL && strcmp(result, NON_EXISTENT_TRASH_ENTRY) == 0) {
 
1914
                /* This means that we know there is no Trash */
 
1915
                g_free (result);
 
1916
                result = NULL;
 
1917
        }
 
1918
 
 
1919
        G_UNLOCK (cached_trash_directories);
 
1920
        
 
1921
        return result;
 
1922
}
 
1923
 
 
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,
 
1931
                   guint permissions,
 
1932
                   GnomeVFSContext *context)
 
1933
{
 
1934
        gint retval;
 
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;
 
1941
 
 
1942
        
 
1943
        target_directory_path = NULL;
 
1944
        *result_uri = NULL;
 
1945
 
 
1946
        full_name_near = get_path_from_uri (near_uri);
 
1947
        if (full_name_near == NULL)
 
1948
                return GNOME_VFS_ERROR_INVALID_URI;
 
1949
 
 
1950
        /* We will need the URI and the stat structure for the home directory. */
 
1951
        home_directory = gnome_vfs_get_home_dir ();
 
1952
 
 
1953
        if (gnome_vfs_context_check_cancellation (context)) {
 
1954
                g_free (full_name_near);
 
1955
                return GNOME_VFS_ERROR_CANCELLED;
 
1956
        }
 
1957
 
 
1958
        retval = g_lstat (full_name_near, &near_item_stat);
 
1959
        if (retval != 0) {
 
1960
                g_free (full_name_near);
 
1961
                return gnome_vfs_result_from_errno ();
 
1962
        }
 
1963
 
 
1964
        if (gnome_vfs_context_check_cancellation (context)) {
 
1965
                g_free (full_name_near);
 
1966
                return GNOME_VFS_ERROR_CANCELLED;
 
1967
        }
 
1968
        
 
1969
        retval = g_stat (home_directory, &home_volume_stat);
 
1970
        if (retval != 0) {
 
1971
                g_free (full_name_near);
 
1972
                return gnome_vfs_result_from_errno ();
 
1973
        }
 
1974
        
 
1975
        if (gnome_vfs_context_check_cancellation (context)) {
 
1976
                g_free (full_name_near);
 
1977
                return GNOME_VFS_ERROR_CANCELLED;
 
1978
        }
 
1979
 
 
1980
        switch (kind) {
 
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.
 
1985
                 */
 
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.
 
1990
                         */
 
1991
                        if (gnome_vfs_context_check_cancellation (context))
 
1992
                                return GNOME_VFS_ERROR_CANCELLED;
 
1993
 
 
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);
 
1997
 
 
1998
                        if (gnome_vfs_context_check_cancellation (context)) {
 
1999
                                return GNOME_VFS_ERROR_CANCELLED;
 
2000
                        }
 
2001
                } else  {
 
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);
 
2004
                }
 
2005
                break;
 
2006
                
 
2007
        case GNOME_VFS_DIRECTORY_KIND_DESKTOP:
 
2008
                if (near_item_stat.st_dev != home_volume_stat.st_dev) {
 
2009
                        /* unsupported */
 
2010
                        break;
 
2011
                }
 
2012
                target_directory_path = append_to_path (home_directory, "Desktop");
 
2013
                break;
 
2014
 
 
2015
        default:
 
2016
                break;
 
2017
        }
 
2018
 
 
2019
        g_free (full_name_near);
 
2020
 
 
2021
        if (target_directory_path == NULL) {
 
2022
                return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2023
        }
 
2024
 
 
2025
        if (create_if_needed && g_access (target_directory_path, F_OK) != 0) {
 
2026
                mkdir_recursive (target_directory_path, permissions);
 
2027
        }
 
2028
 
 
2029
        if (g_access (target_directory_path, F_OK) != 0) {
 
2030
                g_free (target_directory_path);
 
2031
                return GNOME_VFS_ERROR_NOT_FOUND;
 
2032
        }
 
2033
 
 
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);
 
2038
 
 
2039
        return GNOME_VFS_OK;
 
2040
}
 
2041
 
 
2042
static GnomeVFSResult
 
2043
rename_helper (const gchar *old_full_name,
 
2044
               const gchar *new_full_name,
 
2045
               gboolean force_replace,
 
2046
               GnomeVFSContext *context)
 
2047
{
 
2048
        gboolean old_exists;
 
2049
        struct stat statbuf;
 
2050
        gint retval;
 
2051
        gchar *temp_name;
 
2052
        GnomeVFSHandle *temp_handle;
 
2053
        GnomeVFSResult result;
 
2054
 
 
2055
        retval = g_stat (new_full_name, &statbuf);
 
2056
        if (retval == 0) {
 
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.
 
2060
                 */
 
2061
                if (g_ascii_strcasecmp (old_full_name, new_full_name) == 0
 
2062
                    && strcmp (old_full_name, new_full_name) != 0 && ! force_replace) {
 
2063
 
 
2064
                        if (gnome_vfs_context_check_cancellation (context))
 
2065
                                return GNOME_VFS_ERROR_CANCELLED;
 
2066
                        
 
2067
                        result = gnome_vfs_create_temp (old_full_name, &temp_name, &temp_handle);
 
2068
                        if (result != GNOME_VFS_OK)
 
2069
                                return result;
 
2070
                        gnome_vfs_close (temp_handle);
 
2071
                        g_unlink (temp_name);
 
2072
                        
 
2073
                        retval = g_rename (old_full_name, temp_name);
 
2074
                        if (retval == 0) {
 
2075
                                if (g_stat (new_full_name, &statbuf) != 0 
 
2076
                                    && g_rename (temp_name, new_full_name) == 0) {
 
2077
                                        /* Success */
 
2078
                                        return GNOME_VFS_OK;
 
2079
                                }
 
2080
                                /* Revert the filename back to original */ 
 
2081
                                retval = g_rename (temp_name, old_full_name);
 
2082
                                if (retval == 0) {
 
2083
                                        return GNOME_VFS_ERROR_FILE_EXISTS;
 
2084
                                }
 
2085
                        }
 
2086
                        return gnome_vfs_result_from_errno_code (retval);
 
2087
                
 
2088
                } else if (! force_replace) {
 
2089
                        /* If we are not allowed to replace an existing file, 
 
2090
                         * return an error.
 
2091
                         */
 
2092
                        return GNOME_VFS_ERROR_FILE_EXISTS;
 
2093
                }
 
2094
                old_exists = TRUE;
 
2095
        } else {
 
2096
                old_exists = FALSE;
 
2097
        }
 
2098
 
 
2099
        if (gnome_vfs_context_check_cancellation (context))
 
2100
                return GNOME_VFS_ERROR_CANCELLED;
 
2101
 
 
2102
#ifdef G_OS_WIN32
 
2103
        if (force_replace && old_exists)
 
2104
                g_remove (new_full_name);
 
2105
#endif
 
2106
 
 
2107
        retval = g_rename (old_full_name, new_full_name);
 
2108
 
 
2109
#ifndef G_OS_WIN32
 
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
 
2115
         * old and new file.
 
2116
         */
 
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);
 
2126
                        if (retval != 0) {
 
2127
                                return gnome_vfs_result_from_errno ();
 
2128
                        }
 
2129
 
 
2130
                        if (gnome_vfs_context_check_cancellation (context))
 
2131
                                return GNOME_VFS_ERROR_CANCELLED;
 
2132
 
 
2133
                        retval = g_rename (old_full_name, new_full_name);
 
2134
                }
 
2135
        }
 
2136
#endif
 
2137
        if (retval != 0) {
 
2138
                return gnome_vfs_result_from_errno ();
 
2139
        }
 
2140
 
 
2141
        return GNOME_VFS_OK;
 
2142
}
 
2143
 
 
2144
static GnomeVFSResult
 
2145
set_symlink_name_helper (const gchar *full_name,
 
2146
                         const GnomeVFSFileInfo *info)
 
2147
{
 
2148
#ifndef G_OS_WIN32
 
2149
        struct stat statbuf;
 
2150
 
 
2151
        if (info->symlink_name == NULL) {
 
2152
                return GNOME_VFS_ERROR_BAD_PARAMETERS;
 
2153
        }
 
2154
 
 
2155
        if (g_lstat (full_name, &statbuf) != 0) {
 
2156
                return gnome_vfs_result_from_errno ();
 
2157
        }
 
2158
 
 
2159
        if (!S_ISLNK (statbuf.st_mode)) {
 
2160
                return GNOME_VFS_ERROR_NOT_A_SYMBOLIC_LINK;
 
2161
        }
 
2162
 
 
2163
        if (g_unlink (full_name) != 0) {
 
2164
                return gnome_vfs_result_from_errno ();
 
2165
        }
 
2166
 
 
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 ();
 
2170
        }
 
2171
 
 
2172
        return GNOME_VFS_OK;
 
2173
#else
 
2174
        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2175
#endif
 
2176
 
 
2177
}
 
2178
 
 
2179
static GnomeVFSResult
 
2180
do_move (GnomeVFSMethod *method,
 
2181
         GnomeVFSURI *old_uri,
 
2182
         GnomeVFSURI *new_uri,
 
2183
         gboolean force_replace,
 
2184
         GnomeVFSContext *context)
 
2185
{
 
2186
        gchar *old_full_name;
 
2187
        gchar *new_full_name;
 
2188
        GnomeVFSResult result;
 
2189
 
 
2190
        old_full_name = get_path_from_uri (old_uri);
 
2191
        if (old_full_name == NULL)
 
2192
                return GNOME_VFS_ERROR_INVALID_URI;
 
2193
 
 
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;
 
2198
        }
 
2199
 
 
2200
        result = rename_helper (old_full_name, new_full_name,
 
2201
                                force_replace, context);
 
2202
 
 
2203
        g_free (old_full_name);
 
2204
        g_free (new_full_name);
 
2205
 
 
2206
        return result;
 
2207
}
 
2208
 
 
2209
static GnomeVFSResult
 
2210
do_unlink (GnomeVFSMethod *method,
 
2211
           GnomeVFSURI *uri,
 
2212
           GnomeVFSContext *context)
 
2213
{
 
2214
        gchar *full_name;
 
2215
        gint retval;
 
2216
 
 
2217
        full_name = get_path_from_uri (uri);
 
2218
        if (full_name == NULL) {
 
2219
                return GNOME_VFS_ERROR_INVALID_URI;
 
2220
        }
 
2221
 
 
2222
        retval = g_unlink (full_name);
 
2223
 
 
2224
        g_free (full_name);
 
2225
 
 
2226
        if (retval != 0) {
 
2227
                return gnome_vfs_result_from_errno ();
 
2228
        }
 
2229
 
 
2230
        return GNOME_VFS_OK;
 
2231
}
 
2232
 
 
2233
static GnomeVFSResult
 
2234
do_create_symbolic_link (GnomeVFSMethod *method,
 
2235
                         GnomeVFSURI *uri,
 
2236
                         const char *target_reference,
 
2237
                         GnomeVFSContext *context)
 
2238
{
 
2239
#ifndef G_OS_WIN32
 
2240
        const char *link_scheme, *target_scheme;
 
2241
        char *link_full_name, *target_full_name;
 
2242
        GnomeVFSResult result;
 
2243
        GnomeVFSURI *target_uri;
 
2244
 
 
2245
        g_assert (target_reference != NULL);
 
2246
        g_assert (uri != NULL);
 
2247
        
 
2248
        /* what we actually want is a function that takes a const char * and 
 
2249
         * tells whether it is a valid URI
 
2250
         */
 
2251
        target_uri = gnome_vfs_uri_new (target_reference);
 
2252
        if (target_uri == NULL) {
 
2253
                return GNOME_VFS_ERROR_INVALID_URI;
 
2254
        }
 
2255
 
 
2256
        link_scheme = gnome_vfs_uri_get_scheme (uri);
 
2257
        g_assert (link_scheme != NULL);
 
2258
 
 
2259
        target_scheme = gnome_vfs_uri_get_scheme (target_uri);
 
2260
        if (target_scheme == NULL) {
 
2261
                target_scheme = "file";
 
2262
        }
 
2263
        
 
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); 
 
2269
                } else {
 
2270
                        target_full_name = get_path_from_uri (target_uri);
 
2271
                }
 
2272
 
 
2273
                link_full_name = get_path_from_uri (uri);
 
2274
 
 
2275
                if (symlink (target_full_name, link_full_name) != 0) {
 
2276
                        result = gnome_vfs_result_from_errno ();
 
2277
                } else {
 
2278
                        result = GNOME_VFS_OK;
 
2279
                }
 
2280
 
 
2281
                g_free (target_full_name);
 
2282
                g_free (link_full_name);
 
2283
        } else {
 
2284
                /* FIXME bugzilla.eazel.com 2792: do a URI link */
 
2285
                result = GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2286
        }
 
2287
 
 
2288
        gnome_vfs_uri_unref (target_uri);
 
2289
 
 
2290
        return result;
 
2291
#else
 
2292
        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2293
#endif
 
2294
}
 
2295
 
 
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)
 
2309
{
 
2310
        gchar *full_name_source, *full_name_target;
 
2311
        struct stat s_source, s_target;
 
2312
        gint retval;
 
2313
 
 
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);
 
2317
 
 
2318
        if (retval != 0)
 
2319
                return gnome_vfs_result_from_errno ();
 
2320
 
 
2321
        if (gnome_vfs_context_check_cancellation (context))
 
2322
                return GNOME_VFS_ERROR_CANCELLED;
 
2323
 
 
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);
 
2327
 
 
2328
        if (retval != 0)
 
2329
                return gnome_vfs_result_from_errno ();
 
2330
 
 
2331
        *same_fs_return = (s_source.st_dev == s_target.st_dev);
 
2332
 
 
2333
        return GNOME_VFS_OK;
 
2334
}
 
2335
 
 
2336
static GnomeVFSResult
 
2337
do_set_file_info (GnomeVFSMethod *method,
 
2338
                  GnomeVFSURI *uri,
 
2339
                  const GnomeVFSFileInfo *info,
 
2340
                  GnomeVFSSetFileInfoMask mask,
 
2341
                  GnomeVFSContext *context)
 
2342
{
 
2343
        gchar *full_name;
 
2344
 
 
2345
        full_name = get_path_from_uri (uri);
 
2346
        if (full_name == NULL)
 
2347
                return GNOME_VFS_ERROR_INVALID_URI;
 
2348
 
 
2349
        if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
 
2350
                GnomeVFSResult result;
 
2351
                gchar *dir, *encoded_dir;
 
2352
                gchar *new_name;
 
2353
 
 
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);
 
2358
 
 
2359
                new_name = g_build_filename (dir, info->name, NULL);
 
2360
 
 
2361
                result = rename_helper (full_name, new_name, FALSE, context);
 
2362
                
 
2363
                g_free (dir);
 
2364
                g_free (full_name);
 
2365
                full_name = new_name;
 
2366
 
 
2367
                if (result != GNOME_VFS_OK) {
 
2368
                        g_free (full_name);
 
2369
                        return result;
 
2370
                }
 
2371
        }
 
2372
 
 
2373
        if (mask & GNOME_VFS_SET_FILE_INFO_SELINUX_CONTEXT) {
 
2374
                GnomeVFSResult result = set_selinux_context(info, full_name);
 
2375
                if (result < 0) {
 
2376
                        g_free(full_name);
 
2377
                        return result;
 
2378
                }
 
2379
        }
 
2380
 
 
2381
        if (gnome_vfs_context_check_cancellation (context)) {
 
2382
                g_free (full_name);
 
2383
                return GNOME_VFS_ERROR_CANCELLED;
 
2384
        }
 
2385
 
 
2386
        if (mask & GNOME_VFS_SET_FILE_INFO_PERMISSIONS) {
 
2387
                if (chmod (full_name, info->permissions) != 0) {
 
2388
                        g_free (full_name);
 
2389
                        return gnome_vfs_result_from_errno ();
 
2390
                }
 
2391
        }
 
2392
 
 
2393
        if (gnome_vfs_context_check_cancellation (context)) {
 
2394
                g_free (full_name);
 
2395
                return GNOME_VFS_ERROR_CANCELLED;
 
2396
        }
 
2397
 
 
2398
        if (mask & GNOME_VFS_SET_FILE_INFO_OWNER) {
 
2399
#ifndef G_OS_WIN32
 
2400
                if (chown (full_name, info->uid, info->gid) != 0) {
 
2401
                        g_free (full_name);
 
2402
                        return gnome_vfs_result_from_errno ();
 
2403
                }
 
2404
#else
 
2405
                g_warning ("Not implemented: GNOME_VFS_SET_FILE_INFO_OWNER");
 
2406
#endif
 
2407
        }
 
2408
        
 
2409
        if (gnome_vfs_context_check_cancellation (context)) {
 
2410
                g_free (full_name);
 
2411
                return GNOME_VFS_ERROR_CANCELLED;
 
2412
        }
 
2413
 
 
2414
        if (mask & GNOME_VFS_SET_FILE_INFO_TIME) {
 
2415
                struct utimbuf utimbuf;
 
2416
 
 
2417
                utimbuf.actime = info->atime;
 
2418
                utimbuf.modtime = info->mtime;
 
2419
 
 
2420
                if (utime (full_name, &utimbuf) != 0) {
 
2421
                        g_free (full_name);
 
2422
                        return gnome_vfs_result_from_errno ();
 
2423
                }
 
2424
        }
 
2425
 
 
2426
        if (gnome_vfs_context_check_cancellation (context)) {
 
2427
                g_free (full_name);
 
2428
                return GNOME_VFS_ERROR_CANCELLED;
 
2429
        }
 
2430
        
 
2431
        if (mask & GNOME_VFS_SET_FILE_INFO_ACL) {
 
2432
                GnomeVFSResult result;
 
2433
 
 
2434
                result = file_set_acl (full_name, info, context);
 
2435
                if (result != GNOME_VFS_OK) {
 
2436
                        g_free (full_name);
 
2437
                        return result;  
 
2438
                }
 
2439
        }
 
2440
 
 
2441
        if (mask & GNOME_VFS_SET_FILE_INFO_SYMLINK_NAME) {
 
2442
                GnomeVFSResult result;
 
2443
 
 
2444
                result = set_symlink_name_helper (full_name, info);
 
2445
                if (result != GNOME_VFS_OK) {
 
2446
                        g_free (full_name);
 
2447
                        return result;  
 
2448
                }
 
2449
        }
 
2450
 
 
2451
        g_free (full_name);
 
2452
 
 
2453
        return GNOME_VFS_OK;
 
2454
}
 
2455
 
 
2456
#ifdef HAVE_FAM
 
2457
static gboolean
 
2458
fam_do_iter_unlocked (void)
 
2459
{
 
2460
        while (fam_connection != NULL && FAMPending(fam_connection)) {
 
2461
                FAMEvent ev;
 
2462
                FileMonitorHandle *handle;
 
2463
                gboolean cancelled;
 
2464
                GnomeVFSMonitorEventType event_type;
 
2465
 
 
2466
                if (FAMNextEvent(fam_connection, &ev) != 1) {
 
2467
                        FAMClose(fam_connection);
 
2468
                        g_free(fam_connection);
 
2469
                        g_source_remove (fam_watch_id);
 
2470
                        fam_watch_id = 0;
 
2471
                        fam_connection = NULL;
 
2472
                        return FALSE;
 
2473
                }
 
2474
 
 
2475
                handle = (FileMonitorHandle *)ev.userdata;
 
2476
                cancelled = handle->cancelled;
 
2477
                event_type = -1;
 
2478
 
 
2479
                switch (ev.code) {
 
2480
                        case FAMChanged:
 
2481
                                event_type = GNOME_VFS_MONITOR_EVENT_CHANGED;
 
2482
                                break;
 
2483
                        case FAMDeleted:
 
2484
                                event_type = GNOME_VFS_MONITOR_EVENT_DELETED;
 
2485
                                break;
 
2486
                        case FAMStartExecuting:
 
2487
                                event_type = GNOME_VFS_MONITOR_EVENT_STARTEXECUTING;
 
2488
                                break;
 
2489
                        case FAMStopExecuting:
 
2490
                                event_type = GNOME_VFS_MONITOR_EVENT_STOPEXECUTING;
 
2491
                                break;
 
2492
                        case FAMCreated:
 
2493
                                event_type = GNOME_VFS_MONITOR_EVENT_CREATED;
 
2494
                                break;
 
2495
                        case FAMAcknowledge:
 
2496
                                if (handle->cancelled) {
 
2497
                                        gnome_vfs_uri_unref (handle->uri);
 
2498
                                        g_free (handle);
 
2499
                                }
 
2500
                                break;
 
2501
                        case FAMExists:
 
2502
                        case FAMEndExist:
 
2503
                        case FAMMoved:
 
2504
                                /* Not supported */
 
2505
                                break;
 
2506
                }
 
2507
 
 
2508
                if (event_type != -1 && !cancelled) {
 
2509
                        GnomeVFSURI *info_uri;
 
2510
                        gchar *info_str;
 
2511
 
 
2512
                        /* 
 
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 '/'.  
 
2516
                         */
 
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);
 
2520
                                g_free (info_str);
 
2521
                        } else
 
2522
                                info_uri = gnome_vfs_uri_append_file_name (handle->uri, ev.filename);
 
2523
 
 
2524
                        /* This queues an idle, so there are no reentrancy issues */
 
2525
                        gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
 
2526
                                                    info_uri, 
 
2527
                                                    event_type);
 
2528
                        gnome_vfs_uri_unref (info_uri);
 
2529
                }
 
2530
        }
 
2531
 
 
2532
        return TRUE;
 
2533
}
 
2534
 
 
2535
static gboolean
 
2536
fam_callback (GIOChannel *source,
 
2537
              GIOCondition condition,
 
2538
              gpointer data)
 
2539
{
 
2540
        gboolean res;
 
2541
        G_LOCK (fam_connection);
 
2542
 
 
2543
        res = fam_do_iter_unlocked ();
 
2544
 
 
2545
        G_UNLOCK (fam_connection);
 
2546
 
 
2547
        return res;
 
2548
}
 
2549
 
 
2550
 
 
2551
 
 
2552
static gboolean
 
2553
monitor_setup (void)
 
2554
{
 
2555
        GIOChannel *ioc;
 
2556
 
 
2557
        G_LOCK (fam_connection);
 
2558
 
 
2559
        if (fam_connection == NULL) {
 
2560
                fam_connection = g_malloc0(sizeof(FAMConnection));
 
2561
                if (FAMOpen2(fam_connection, "gnome-vfs user") != 0) {
 
2562
#ifdef DEBUG_FAM
 
2563
                        g_print ("FAMOpen failed, FAMErrno=%d\n", FAMErrno);
 
2564
#endif
 
2565
                        g_free(fam_connection);
 
2566
                        fam_connection = NULL;
 
2567
                        G_UNLOCK (fam_connection);
 
2568
                        return FALSE;
 
2569
                }
 
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);
 
2575
        }
 
2576
 
 
2577
        G_UNLOCK (fam_connection);
 
2578
 
 
2579
        return TRUE;
 
2580
}
 
2581
#endif
 
2582
 
 
2583
#ifdef HAVE_FAM
 
2584
static GnomeVFSResult
 
2585
fam_monitor_cancel (GnomeVFSMethod *method,
 
2586
                    GnomeVFSMethodHandle *method_handle)
 
2587
{
 
2588
        FileMonitorHandle *handle = (FileMonitorHandle *)method_handle;
 
2589
 
 
2590
        if (!monitor_setup ()) {
 
2591
                return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2592
        }
 
2593
 
 
2594
        if (handle->cancelled)
 
2595
                return GNOME_VFS_OK;
 
2596
 
 
2597
        handle->cancelled = TRUE;
 
2598
        G_LOCK (fam_connection);
 
2599
 
 
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 ();
 
2603
 
 
2604
        if (fam_connection == NULL) {
 
2605
                G_UNLOCK (fam_connection);
 
2606
                return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2607
        }
 
2608
        
 
2609
        FAMCancelMonitor (fam_connection, &handle->request);
 
2610
        G_UNLOCK (fam_connection);
 
2611
 
 
2612
        return GNOME_VFS_OK;
 
2613
}
 
2614
 
 
2615
 
 
2616
static GnomeVFSResult
 
2617
fam_monitor_add (GnomeVFSMethod *method,
 
2618
                 GnomeVFSMethodHandle **method_handle_return,
 
2619
                 GnomeVFSURI *uri,
 
2620
                 GnomeVFSMonitorType monitor_type)
 
2621
{
 
2622
        FileMonitorHandle *handle;
 
2623
        char *filename;
 
2624
 
 
2625
        if (!monitor_setup ()) {
 
2626
                return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2627
        }
 
2628
 
 
2629
        filename = get_path_from_uri (uri);
 
2630
        if (filename == NULL) {
 
2631
                return GNOME_VFS_ERROR_INVALID_URI;
 
2632
        }
 
2633
        
 
2634
        handle = g_new0 (FileMonitorHandle, 1);
 
2635
        handle->cancel_func = fam_monitor_cancel;
 
2636
        handle->uri = uri;
 
2637
        handle->cancelled = FALSE;
 
2638
        gnome_vfs_uri_ref (uri);
 
2639
 
 
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 ();
 
2644
 
 
2645
        if (fam_connection == NULL) {
 
2646
                G_UNLOCK (fam_connection);
 
2647
                g_free (handle);
 
2648
                gnome_vfs_uri_unref (uri);
 
2649
                g_free (filename);
 
2650
                return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2651
        }
 
2652
        
 
2653
        if (monitor_type == GNOME_VFS_MONITOR_FILE) {
 
2654
                FAMMonitorFile (fam_connection, filename, 
 
2655
                        &handle->request, handle);
 
2656
        } else {
 
2657
                FAMMonitorDirectory (fam_connection, filename, 
 
2658
                        &handle->request, handle);
 
2659
        }
 
2660
 
 
2661
        G_UNLOCK (fam_connection);
 
2662
        
 
2663
        *method_handle_return = (GnomeVFSMethodHandle *)handle;
 
2664
 
 
2665
        g_free (filename);
 
2666
 
 
2667
        return GNOME_VFS_OK;
 
2668
 
 
2669
}
 
2670
#endif
 
2671
 
 
2672
#ifdef USE_INOTIFY
 
2673
 
 
2674
static GnomeVFSResult
 
2675
inotify_monitor_cancel (GnomeVFSMethod *method,
 
2676
                        GnomeVFSMethodHandle *method_handle)
 
2677
{
 
2678
        ih_sub_t *sub = (ih_sub_t *)method_handle;
 
2679
 
 
2680
        if (sub->cancelled)
 
2681
                return GNOME_VFS_OK;
 
2682
 
 
2683
        ih_sub_cancel (sub);
 
2684
        ih_sub_free (sub);
 
2685
        return GNOME_VFS_OK;
 
2686
 
 
2687
}
 
2688
 
 
2689
static GnomeVFSResult
 
2690
inotify_monitor_add (GnomeVFSMethod *method,
 
2691
                     GnomeVFSMethodHandle **method_handle_return,
 
2692
                     GnomeVFSURI *uri,
 
2693
                     GnomeVFSMonitorType monitor_type)
 
2694
{
 
2695
        ih_sub_t *sub;
 
2696
 
 
2697
        sub = ih_sub_new (uri, monitor_type);
 
2698
        if (sub == NULL) {
 
2699
                return GNOME_VFS_ERROR_INVALID_URI;
 
2700
        }
 
2701
        sub->cancel_func = inotify_monitor_cancel;
 
2702
        if (ih_sub_add (sub) == FALSE) {
 
2703
                ih_sub_free (sub);
 
2704
                *method_handle_return = NULL;
 
2705
                return GNOME_VFS_ERROR_INVALID_URI;
 
2706
        }
 
2707
 
 
2708
        *method_handle_return = (GnomeVFSMethodHandle *)sub;
 
2709
        return GNOME_VFS_OK;
 
2710
 
 
2711
}
 
2712
#endif
 
2713
 
 
2714
static GnomeVFSResult
 
2715
do_monitor_add (GnomeVFSMethod *method,
 
2716
                GnomeVFSMethodHandle **method_handle_return,
 
2717
                GnomeVFSURI *uri,
 
2718
                GnomeVFSMonitorType monitor_type)
 
2719
{
 
2720
#ifdef USE_INOTIFY
 
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);
 
2724
        }
 
2725
#endif
 
2726
#ifdef HAVE_FAM
 
2727
        return fam_monitor_add (method, method_handle_return, uri, monitor_type);
 
2728
#endif
 
2729
        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2730
}
 
2731
 
 
2732
static GnomeVFSResult
 
2733
do_monitor_cancel (GnomeVFSMethod *method,
 
2734
                   GnomeVFSMethodHandle *method_handle)
 
2735
{
 
2736
        AnyFileMonitorHandle *handle;
 
2737
 
 
2738
        handle = (AnyFileMonitorHandle *)method_handle;
 
2739
        if (handle != NULL) {
 
2740
                return (handle->cancel_func) (method, method_handle);
 
2741
        }
 
2742
        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2743
}
 
2744
 
 
2745
static GnomeVFSResult
 
2746
do_file_control (GnomeVFSMethod *method,
 
2747
                 GnomeVFSMethodHandle *method_handle,
 
2748
                 const char *operation,
 
2749
                 gpointer operation_data,
 
2750
                 GnomeVFSContext *context)
 
2751
{
 
2752
        if (strcmp (operation, "file:test") == 0) {
 
2753
                *(char **)operation_data = g_strdup ("test ok");
 
2754
                return GNOME_VFS_OK;
 
2755
        }
 
2756
        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2757
}
 
2758
 
 
2759
static GnomeVFSResult
 
2760
do_get_volume_free_space (GnomeVFSMethod *method,
 
2761
                          const GnomeVFSURI *uri,
 
2762
                          GnomeVFSFileSize *free_space)
 
2763
{
 
2764
#ifndef G_OS_WIN32
 
2765
        GnomeVFSFileSize free_blocks, block_size;
 
2766
        int statfs_result;
 
2767
        const char *path;
 
2768
        char *unescaped_path;
 
2769
 
 
2770
#if HAVE_STATVFS
 
2771
        struct statvfs statfs_buffer;
 
2772
#else
 
2773
        struct statfs statfs_buffer;
 
2774
#endif
 
2775
 
 
2776
        *free_space = 0;
 
2777
 
 
2778
        path = gnome_vfs_uri_get_path (uri);
 
2779
        if (path == NULL || *path != '/') {
 
2780
                return GNOME_VFS_ERROR_INVALID_URI;
 
2781
        }
 
2782
 
 
2783
        unescaped_path = gnome_vfs_unescape_string (path, G_DIR_SEPARATOR_S);
 
2784
        
 
2785
#if HAVE_STATVFS
 
2786
        statfs_result = statvfs (unescaped_path, &statfs_buffer);
 
2787
        block_size = statfs_buffer.f_frsize; 
 
2788
#else
 
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);
 
2794
#endif
 
2795
        block_size = statfs_buffer.f_bsize; 
 
2796
#endif  
 
2797
 
 
2798
        if (statfs_result != 0) {
 
2799
                g_free (unescaped_path);
 
2800
                return gnome_vfs_result_from_errno ();
 
2801
        }
 
2802
 
 
2803
 
 
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
 
2806
 * (2004-03-08)
 
2807
 */
 
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
 
2812
                 * with statfs.
 
2813
                 */
 
2814
                struct statfs statfs_buffer2;
 
2815
                statfs_result = statfs (unescaped_path, &statfs_buffer2);
 
2816
                g_free (unescaped_path);
 
2817
 
 
2818
                if (statfs_result != 0) {
 
2819
                        return gnome_vfs_result_from_errno ();
 
2820
                }
 
2821
                
 
2822
                /* linux/ncp_fs.h: NCP_SUPER_MAGIC == 0x564c */
 
2823
                if (statfs_buffer2.f_type == 0x564c)
 
2824
                {
 
2825
                        return GNOME_VFS_ERROR_NOT_SUPPORTED;
 
2826
                }
 
2827
        } else {
 
2828
                /* everything is copacetic... free the unescaped path */
 
2829
                g_free (unescaped_path);
 
2830
        }
 
2831
#else   
 
2832
        g_free (unescaped_path);
 
2833
#endif
 
2834
        free_blocks = statfs_buffer.f_bavail;
 
2835
 
 
2836
        *free_space = block_size * free_blocks;
 
2837
        
 
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;
 
2842
#endif
 
2843
}
 
2844
 
 
2845
 
 
2846
static GnomeVFSMethod method = {
 
2847
        sizeof (GnomeVFSMethod),
 
2848
        do_open,
 
2849
        do_create,
 
2850
        do_close,
 
2851
        do_read,
 
2852
        do_write,
 
2853
        do_seek,
 
2854
        do_tell,
 
2855
        do_truncate_handle,
 
2856
        do_open_directory,
 
2857
        do_close_directory,
 
2858
        do_read_directory,
 
2859
        do_get_file_info,
 
2860
        do_get_file_info_from_handle,
 
2861
        do_is_local,
 
2862
        do_make_directory,
 
2863
        do_remove_directory,
 
2864
        do_move,
 
2865
        do_unlink,
 
2866
        do_check_same_fs,
 
2867
        do_set_file_info,
 
2868
        do_truncate,
 
2869
        do_find_directory,
 
2870
        do_create_symbolic_link,
 
2871
        do_monitor_add,
 
2872
        do_monitor_cancel,
 
2873
        do_file_control,
 
2874
        do_forget_cache,
 
2875
        do_get_volume_free_space
 
2876
};
 
2877
 
 
2878
GnomeVFSMethod *
 
2879
vfs_module_init (const char *method_name, const char *args)
 
2880
{
 
2881
        return &method;
 
2882
}
 
2883
 
 
2884
void
 
2885
vfs_module_shutdown (GnomeVFSMethod *method)
 
2886
{
 
2887
}