1
/* GIO - GLib Input, Output and Streaming Library
3
* Copyright (C) 2006-2007 Red Hat, Inc.
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU Lesser General
16
* Public License along with this library; if not, write to the
17
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18
* Boston, MA 02111-1307, USA.
20
* Author: Alexander Larsson <alexl@redhat.com>
25
#include <sys/types.h>
33
#include <glib/gstdio.h>
34
#include <glib/gi18n.h>
37
#include "gvfsbackendsmb.h"
38
#include "gvfsjobopenforread.h"
39
#include "gvfsjobread.h"
40
#include "gvfsjobseekread.h"
41
#include "gvfsjobopenforwrite.h"
42
#include "gvfsjobwrite.h"
43
#include "gvfsjobclosewrite.h"
44
#include "gvfsjobseekwrite.h"
45
#include "gvfsjobsetdisplayname.h"
46
#include "gvfsjobqueryinfo.h"
47
#include "gvfsjobqueryfsinfo.h"
48
#include "gvfsjobqueryattributes.h"
49
#include "gvfsjobenumerate.h"
50
#include "gvfsdaemonprotocol.h"
51
#include "gvfskeyring.h"
53
#include <libsmbclient.h>
54
#include "libsmb-compat.h"
56
struct _GVfsBackendSmb
58
GVfsBackend parent_instance;
65
char *default_workgroup;
73
GMountSource *mount_source; /* Only used/set during mount */
75
gboolean mount_try_again;
76
gboolean mount_cancelled;
78
gboolean password_in_keyring;
79
GPasswordSave password_save;
82
char *cached_server_name;
83
char *cached_share_name;
85
char *cached_username;
86
SMBCSRV *cached_server;
90
G_DEFINE_TYPE (GVfsBackendSmb, g_vfs_backend_smb, G_VFS_TYPE_BACKEND)
92
static void set_info_from_stat (GVfsBackendSmb *backend,
96
GFileAttributeMatcher *matcher);
100
g_vfs_backend_smb_finalize (GObject *object)
102
GVfsBackendSmb *backend;
104
backend = G_VFS_BACKEND_SMB (object);
106
g_free (backend->share);
107
g_free (backend->server);
108
g_free (backend->user);
109
g_free (backend->domain);
110
g_free (backend->path);
111
g_free (backend->default_workgroup);
113
if (G_OBJECT_CLASS (g_vfs_backend_smb_parent_class)->finalize)
114
(*G_OBJECT_CLASS (g_vfs_backend_smb_parent_class)->finalize) (object);
118
g_vfs_backend_smb_init (GVfsBackendSmb *backend)
123
/* Get default workgroup name */
124
settings = g_settings_new ("org.gnome.system.smb");
126
workgroup = g_settings_get_string (settings, "workgroup");
127
if (workgroup && workgroup[0])
128
backend->default_workgroup = workgroup;
132
g_object_unref (settings);
136
* Authentication callback function type (method that includes context)
138
* Type for the the authentication function called by the library to
139
* obtain authentication credentals
141
* @param context Pointer to the smb context
142
* @param srv Server being authenticated to
143
* @param shr Share being authenticated to
144
* @param wg Pointer to buffer containing a "hint" for the
145
* workgroup to be authenticated. Should be filled in
146
* with the correct workgroup if the hint is wrong.
147
* @param wglen The size of the workgroup buffer in bytes
148
* @param un Pointer to buffer containing a "hint" for the
149
* user name to be use for authentication. Should be
150
* filled in with the correct workgroup if the hint is
152
* @param unlen The size of the username buffer in bytes
153
* @param pw Pointer to buffer containing to which password
155
* @param pwlen The size of the password buffer in bytes
159
auth_callback (SMBCCTX *context,
160
const char *server_name, const char *share_name,
161
char *domain_out, int domainmaxlen,
162
char *username_out, int unmaxlen,
163
char *password_out, int pwmaxlen)
165
GVfsBackendSmb *backend;
166
char *ask_password, *ask_user, *ask_domain;
167
gboolean handled, abort;
169
backend = smbc_getOptionUserData (context);
171
strncpy (password_out, "", pwmaxlen);
174
strncpy (domain_out, backend->domain, domainmaxlen);
176
strncpy (username_out, backend->user, unmaxlen);
178
if (backend->mount_cancelled)
180
/* Don't prompt for credentials, let smbclient finish the mount loop */
181
strncpy (username_out, "ABORT", unmaxlen);
182
strncpy (password_out, "", pwmaxlen);
186
if (backend->mount_source == NULL)
188
/* Not during mount, use last password */
189
if (backend->last_user)
190
strncpy (username_out, backend->last_user, unmaxlen);
191
if (backend->last_domain)
192
strncpy (domain_out, backend->last_domain, domainmaxlen);
193
if (backend->last_password)
194
strncpy (password_out, backend->last_password, pwmaxlen);
199
if (backend->mount_try == 0 &&
200
backend->user == NULL &&
201
backend->domain == NULL)
203
/* Try again if kerberos login + anonymous fallback fails */
204
backend->mount_try_again = TRUE;
208
gboolean in_keyring = FALSE;
210
if (!backend->password_in_keyring)
212
in_keyring = g_vfs_keyring_lookup_password (backend->user,
222
backend->password_in_keyring = in_keyring;
227
GAskPasswordFlags flags = G_ASK_PASSWORD_NEED_PASSWORD;
230
if (g_vfs_keyring_is_available ())
231
flags |= G_ASK_PASSWORD_SAVING_SUPPORTED;
232
if (backend->domain == NULL)
233
flags |= G_ASK_PASSWORD_NEED_DOMAIN;
234
if (backend->user == NULL)
235
flags |= G_ASK_PASSWORD_NEED_USERNAME;
237
/* translators: First %s is a share name, second is a server name */
238
message = g_strdup_printf (_("Password required for share %s on %s"),
239
share_name, server_name);
240
handled = g_mount_source_ask_password (backend->mount_source,
250
&(backend->password_save));
257
strncpy (username_out, "ABORT", unmaxlen);
258
strncpy (password_out, "", pwmaxlen);
259
backend->mount_cancelled = TRUE;
264
/* Try again if this fails */
265
backend->mount_try_again = TRUE;
267
strncpy (password_out, ask_password, pwmaxlen);
268
if (ask_user && *ask_user)
269
strncpy (username_out, ask_user, unmaxlen);
270
if (ask_domain && *ask_domain)
271
strncpy (domain_out, ask_domain, domainmaxlen);
274
g_free (ask_password);
279
backend->last_user = g_strdup (username_out);
280
backend->last_domain = g_strdup (domain_out);
281
backend->last_password = g_strdup (password_out);
284
/* Add a server to the cache system
286
* @param c pointer to smb context
287
* @param srv pointer to server to add
288
* @param server server name
289
* @param share share name
290
* @param workgroup workgroup used to connect
291
* @param username username used to connect
292
* @return 0 on success. 1 on failure.
296
add_cached_server (SMBCCTX *context, SMBCSRV *new,
297
const char *server_name, const char *share_name,
298
const char *domain, const char *username)
300
GVfsBackendSmb *backend;
302
backend = smbc_getOptionUserData (context);
304
if (backend->cached_server != NULL)
307
backend->cached_server_name = g_strdup (server_name);
308
backend->cached_share_name = g_strdup (share_name);
309
backend->cached_domain = g_strdup (domain);
310
backend->cached_username = g_strdup (username);
311
backend->cached_server = new;
316
/* Remove cached server
318
* @param c pointer to smb context
319
* @param srv pointer to server to remove
320
* @return 0 when found and removed. 1 on failure.
324
remove_cached_server(SMBCCTX * context, SMBCSRV * server)
326
GVfsBackendSmb *backend;
328
backend = smbc_getOptionUserData (context);
330
if (backend->cached_server == server)
332
g_free (backend->cached_server_name);
333
backend->cached_server_name = NULL;
334
g_free (backend->cached_share_name);
335
backend->cached_share_name = NULL;
336
g_free (backend->cached_domain);
337
backend->cached_domain = NULL;
338
g_free (backend->cached_username);
339
backend->cached_username = NULL;
340
backend->cached_server = NULL;
347
/* Look up a server in the cache system
349
* @param c pointer to smb context
350
* @param server server name to match
351
* @param share share name to match
352
* @param workgroup workgroup to match
353
* @param username username to match
354
* @return pointer to SMBCSRV on success. NULL on failure.
358
get_cached_server (SMBCCTX * context,
359
const char *server_name, const char *share_name,
360
const char *domain, const char *username)
362
GVfsBackendSmb *backend;
364
backend = smbc_getOptionUserData (context);
366
if (backend->cached_server != NULL &&
367
strcmp (backend->cached_server_name, server_name) == 0 &&
368
strcmp (backend->cached_share_name, share_name) == 0 &&
369
strcmp (backend->cached_domain, domain) == 0 &&
370
strcmp (backend->cached_username, username) == 0)
371
return backend->cached_server;
376
/* Try to remove all servers from the cache system and disconnect
378
* @param c pointer to smb context
380
* @return 0 when found and removed. 1 on failure.
384
purge_cached (SMBCCTX * context)
386
GVfsBackendSmb *backend;
388
backend = smbc_getOptionUserData (context);
390
if (backend->cached_server)
391
remove_cached_server(context, backend->cached_server);
396
#define SUB_DELIM_CHARS "!$&'()*+,;="
399
is_valid (char c, const char *reserved_chars_allowed)
401
if (g_ascii_isalnum (c) ||
408
if (reserved_chars_allowed &&
409
strchr (reserved_chars_allowed, c) != NULL)
416
g_string_append_encoded (GString *string,
418
const char *reserved_chars_allowed)
421
static const gchar hex[16] = "0123456789ABCDEF";
423
while ((c = *encoded++) != 0)
425
if (is_valid (c, reserved_chars_allowed))
426
g_string_append_c (string, c);
429
g_string_append_c (string, '%');
430
g_string_append_c (string, hex[((guchar)c) >> 4]);
431
g_string_append_c (string, hex[((guchar)c) & 0xf]);
437
create_smb_uri_string (const char *server,
443
uri = g_string_new ("smb://");
444
g_string_append_encoded (uri, server, NULL);
445
g_string_append_c (uri, '/');
446
g_string_append_encoded (uri, share, NULL);
450
g_string_append_c (uri, '/');
451
g_string_append_encoded (uri, path, SUB_DELIM_CHARS ":@/");
454
while (uri->len > 0 &&
455
uri->str[uri->len - 1] == '/')
456
g_string_erase (uri, uri->len - 1, 1);
462
create_smb_uri (const char *server,
467
uri = create_smb_uri_string (server, share, path);
468
return g_string_free (uri, FALSE);
472
do_mount (GVfsBackend *backend,
474
GMountSpec *mount_spec,
475
GMountSource *mount_source,
476
gboolean is_automount)
478
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
479
SMBCCTX *smb_context;
486
GMountSpec *smb_mount_spec;
487
smbc_stat_fn smbc_stat;
489
smb_context = smbc_new_context ();
490
if (smb_context == NULL)
492
g_vfs_job_failed (G_VFS_JOB (job),
493
G_IO_ERROR, G_IO_ERROR_FAILED,
494
_("Internal Error (%s)"), "Failed to allocate smb context");
497
smbc_setOptionUserData (smb_context, backend);
499
debug = g_getenv ("GVFS_SMB_DEBUG");
501
debug_val = atoi (debug);
505
smbc_setDebug (smb_context, debug_val);
506
smbc_setFunctionAuthDataWithContext (smb_context, auth_callback);
508
smbc_setFunctionAddCachedServer (smb_context, add_cached_server);
509
smbc_setFunctionGetCachedServer (smb_context, get_cached_server);
510
smbc_setFunctionRemoveCachedServer (smb_context, remove_cached_server);
511
smbc_setFunctionPurgeCachedServers (smb_context, purge_cached);
513
/* FIXME: is strdup() still needed here? -- removed */
514
if (op_backend->default_workgroup != NULL)
515
smbc_setWorkgroup (smb_context, op_backend->default_workgroup);
517
#ifndef DEPRECATED_SMBC_INTERFACE
518
smb_context->flags = 0;
522
* - use Kerberos (always)
523
* - in case of no username specified, try anonymous login
525
smbc_setOptionUseKerberos (smb_context, 1);
526
smbc_setOptionFallbackAfterKerberos (smb_context,
527
op_backend->user != NULL);
528
smbc_setOptionNoAutoAnonymousLogin (smb_context,
529
op_backend->user != NULL);
533
smbc_setOptionDebugToStderr (smb_context, 1);
536
if (!smbc_init_context (smb_context))
538
g_vfs_job_failed (G_VFS_JOB (job),
539
G_IO_ERROR, G_IO_ERROR_FAILED,
540
_("Internal Error (%s)"), "Failed to initialize smb context");
541
smbc_free_context (smb_context, FALSE);
545
op_backend->smb_context = smb_context;
547
/* Set the mountspec according to original uri, no matter whether user changes
548
credentials during mount loop. Nautilus and other gio clients depend
549
on correct mountspec, setting it to real (different) credentials would
550
lead to G_IO_ERROR_NOT_MOUNTED errors
553
/* Translators: This is "<sharename> on <servername>" and is used as name for an SMB share */
554
display_name = g_strdup_printf (_("%s on %s"), op_backend->share, op_backend->server);
555
g_vfs_backend_set_display_name (backend, display_name);
556
g_free (display_name);
557
g_vfs_backend_set_icon_name (backend, "folder-remote");
559
smb_mount_spec = g_mount_spec_new ("smb-share");
560
g_mount_spec_set (smb_mount_spec, "share", op_backend->share);
561
g_mount_spec_set (smb_mount_spec, "server", op_backend->server);
562
if (op_backend->user)
563
g_mount_spec_set (smb_mount_spec, "user", op_backend->user);
564
if (op_backend->domain)
565
g_mount_spec_set (smb_mount_spec, "domain", op_backend->domain);
567
g_vfs_backend_set_mount_spec (backend, smb_mount_spec);
568
g_mount_spec_unref (smb_mount_spec);
570
/* FIXME: we're stat()-ing user-specified path here, not the root. Ideally we
571
would like to fallback to root when first mount attempt fails, though
572
it would be tough to actually say if it was an authentication failure
573
or the particular path problem. */
574
uri = create_smb_uri (op_backend->server, op_backend->share, op_backend->path);
577
/* Samba mount loop */
578
op_backend->mount_source = mount_source;
579
op_backend->mount_try = 0;
580
op_backend->password_save = G_PASSWORD_SAVE_NEVER;
584
op_backend->mount_try_again = FALSE;
585
op_backend->mount_cancelled = FALSE;
587
smbc_stat = smbc_getFunctionStat (smb_context);
588
res = smbc_stat (smb_context, uri, &st);
590
if (res == 0 || op_backend->mount_cancelled ||
591
(errno != EACCES && errno != EPERM))
594
/* The first round is Kerberos-only. Only if this fails do we enable
595
* NTLMSSP fallback (turning off anonymous fallback, which we've
596
* already tried and failed with).
598
if (op_backend->mount_try == 0)
600
smbc_setOptionFallbackAfterKerberos (op_backend->smb_context, 1);
601
smbc_setOptionNoAutoAnonymousLogin (op_backend->smb_context, 1);
603
op_backend->mount_try ++;
605
while (op_backend->mount_try_again);
609
op_backend->mount_source = NULL;
613
/* TODO: Error from errno? */
614
op_backend->mount_source = NULL;
616
if (op_backend->mount_cancelled)
617
g_vfs_job_failed (G_VFS_JOB (job),
618
G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED,
619
_("Password dialog cancelled"));
621
g_vfs_job_failed (G_VFS_JOB (job),
622
G_IO_ERROR, G_IO_ERROR_FAILED,
623
/* translators: We tried to mount a windows (samba) share, but failed */
624
_("Failed to mount Windows share"));
629
/* Mount was successful */
631
g_vfs_backend_set_default_location (backend, op_backend->path);
632
g_vfs_keyring_save_password (op_backend->last_user,
634
op_backend->last_domain,
639
op_backend->last_password,
640
op_backend->password_save);
642
g_vfs_job_succeeded (G_VFS_JOB (job));
646
try_mount (GVfsBackend *backend,
648
GMountSpec *mount_spec,
649
GMountSource *mount_source,
650
gboolean is_automount)
652
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
653
const char *server, *share, *user, *domain, *path;
655
server = g_mount_spec_get (mount_spec, "server");
656
share = g_mount_spec_get (mount_spec, "share");
658
if (server == NULL || share == NULL)
660
g_vfs_job_failed (G_VFS_JOB (job),
661
G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
662
_("Invalid mount spec"));
666
user = g_mount_spec_get (mount_spec, "user");
667
domain = g_mount_spec_get (mount_spec, "domain");
668
path = mount_spec->mount_prefix;
670
op_backend->server = g_strdup (server);
671
op_backend->share = g_strdup (share);
672
op_backend->user = g_strdup (user);
673
op_backend->domain = g_strdup (domain);
674
op_backend->path = g_strdup (path);
680
fixup_open_errno (int err)
682
/* samba has a bug (#6228) where it doesn't set errno if path resolving failed */
689
do_open_for_read (GVfsBackend *backend,
690
GVfsJobOpenForRead *job,
691
const char *filename)
693
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
697
smbc_open_fn smbc_open;
698
smbc_stat_fn smbc_stat;
703
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
704
smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
706
file = smbc_open (op_backend->smb_context, uri, O_RDONLY, 0);
710
olderr = fixup_open_errno (errno);
712
smbc_stat = smbc_getFunctionStat (op_backend->smb_context);
713
res = smbc_stat (op_backend->smb_context, uri, &st);
715
if ((res == 0) && (S_ISDIR (st.st_mode)))
716
g_vfs_job_failed (G_VFS_JOB (job),
717
G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
718
_("Can't open directory"));
720
g_vfs_job_failed_from_errno (G_VFS_JOB (job), olderr);
725
g_vfs_job_open_for_read_set_can_seek (job, TRUE);
726
g_vfs_job_open_for_read_set_handle (job, file);
727
g_vfs_job_succeeded (G_VFS_JOB (job));
732
do_read (GVfsBackend *backend,
734
GVfsBackendHandle handle,
736
gsize bytes_requested)
738
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
740
smbc_read_fn smbc_read;
742
/* libsmbclient limits blocksize to (64*1024)-2 for Windows servers,
743
* let's do the same here to achieve reasonable performance. (#588391)
745
* TODO: port to pull mechanism (#592468)
747
if (bytes_requested > 65534)
748
bytes_requested = 65534;
750
smbc_read = smbc_getFunctionRead (op_backend->smb_context);
751
res = smbc_read (op_backend->smb_context, (SMBCFILE *)handle, buffer, bytes_requested);
754
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
757
g_vfs_job_read_set_size (job, res);
758
g_vfs_job_succeeded (G_VFS_JOB (job));
763
do_seek_on_read (GVfsBackend *backend,
764
GVfsJobSeekRead *job,
765
GVfsBackendHandle handle,
769
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
772
smbc_lseek_fn smbc_lseek;
786
g_vfs_job_failed (G_VFS_JOB (job),
787
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
788
_("Unsupported seek type"));
792
smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
793
res = smbc_lseek (op_backend->smb_context, (SMBCFILE *)handle, offset, whence);
795
if (res == (off_t)-1)
796
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
799
g_vfs_job_seek_read_set_offset (job, res);
800
g_vfs_job_succeeded (G_VFS_JOB (job));
807
do_query_info_on_read (GVfsBackend *backend,
808
GVfsJobQueryInfoRead *job,
809
GVfsBackendHandle handle,
811
GFileAttributeMatcher *matcher)
813
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
814
struct stat st = {0};
815
int res, saved_errno;
816
smbc_fstat_fn smbc_fstat;
818
smbc_fstat = smbc_getFunctionFstat (op_backend->smb_context);
819
res = smbc_fstat (op_backend->smb_context, (SMBCFILE *)handle, &st);
824
set_info_from_stat (op_backend, info, &st, NULL, matcher);
826
g_vfs_job_succeeded (G_VFS_JOB (job));
829
g_vfs_job_failed_from_errno (G_VFS_JOB (job), saved_errno);
834
do_close_read (GVfsBackend *backend,
835
GVfsJobCloseRead *job,
836
GVfsBackendHandle handle)
838
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
840
smbc_close_fn smbc_close;
842
smbc_close = smbc_getFunctionClose (op_backend->smb_context);
843
res = smbc_close (op_backend->smb_context, (SMBCFILE *)handle);
845
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
847
g_vfs_job_succeeded (G_VFS_JOB (job));
858
smb_write_handle_free (SmbWriteHandle *handle)
860
g_free (handle->uri);
861
g_free (handle->tmp_uri);
862
g_free (handle->backup_uri);
867
do_create (GVfsBackend *backend,
868
GVfsJobOpenForWrite *job,
869
const char *filename,
870
GFileCreateFlags flags)
872
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
875
SmbWriteHandle *handle;
876
smbc_open_fn smbc_open;
879
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
880
smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
882
file = smbc_open (op_backend->smb_context, uri,
883
O_CREAT|O_WRONLY|O_EXCL, 0666);
888
errsv = fixup_open_errno (errno);
890
/* We guarantee EEXIST on create on existing dir */
893
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
897
handle = g_new0 (SmbWriteHandle, 1);
900
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
901
g_vfs_job_open_for_write_set_handle (job, handle);
902
g_vfs_job_succeeded (G_VFS_JOB (job));
907
do_append_to (GVfsBackend *backend,
908
GVfsJobOpenForWrite *job,
909
const char *filename,
910
GFileCreateFlags flags)
912
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
915
SmbWriteHandle *handle;
916
off_t initial_offset;
917
smbc_open_fn smbc_open;
918
smbc_lseek_fn smbc_lseek;
920
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
921
smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
923
file = smbc_open (op_backend->smb_context, uri,
924
O_CREAT|O_WRONLY|O_APPEND, 0666);
928
g_vfs_job_failed_from_errno (G_VFS_JOB (job), fixup_open_errno (errno));
931
handle = g_new0 (SmbWriteHandle, 1);
934
smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
935
initial_offset = smbc_lseek (op_backend->smb_context, file,
937
if (initial_offset == (off_t) -1)
938
g_vfs_job_open_for_write_set_can_seek (job, FALSE);
941
g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
942
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
944
g_vfs_job_open_for_write_set_handle (job, handle);
945
g_vfs_job_succeeded (G_VFS_JOB (job));
951
random_chars (char *str, int len)
954
const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
956
for (i = 0; i < len; i++)
957
str[i] = chars[g_random_int_range (0, strlen(chars))];
961
get_dir_from_uri (const char *uri)
963
const char *prefix_end;
965
prefix_end = uri + strlen (uri);
967
/* Skip slashes at end */
968
while (prefix_end > uri &&
969
*(prefix_end - 1) == '/')
972
/* Skip to next slash */
973
while (prefix_end > uri &&
974
*(prefix_end - 1) != '/')
977
return g_strndup (uri, prefix_end - uri);
981
open_tmpfile (GVfsBackendSmb *backend,
985
char *dir_uri, *tmp_uri;
986
char filename[] = "~gvfXXXX.tmp";
988
smbc_open_fn smbc_open;
990
dir_uri = get_dir_from_uri (uri);
993
random_chars (filename + 4, 4);
994
tmp_uri = g_strconcat (dir_uri, filename, NULL);
996
smbc_open = smbc_getFunctionOpen (backend->smb_context);
998
file = smbc_open (backend->smb_context, tmp_uri,
999
O_CREAT|O_WRONLY|O_EXCL, 0666);
1000
} while (file == NULL && errno == EEXIST);
1006
*tmp_uri_out = tmp_uri;
1017
copy_file (GVfsBackendSmb *backend,
1019
const char *from_uri,
1022
SMBCFILE *from_file, *to_file;
1028
smbc_open_fn smbc_open;
1029
smbc_read_fn smbc_read;
1030
smbc_write_fn smbc_write;
1031
smbc_close_fn smbc_close;
1039
smbc_open = smbc_getFunctionOpen (backend->smb_context);
1040
smbc_read = smbc_getFunctionRead (backend->smb_context);
1041
smbc_write = smbc_getFunctionWrite (backend->smb_context);
1042
smbc_close = smbc_getFunctionClose (backend->smb_context);
1044
from_file = smbc_open (backend->smb_context, from_uri,
1046
if (from_file == NULL || g_vfs_job_is_cancelled (job))
1049
to_file = smbc_open (backend->smb_context, to_uri,
1050
O_CREAT|O_WRONLY|O_TRUNC, 0666);
1052
if (from_file == NULL || g_vfs_job_is_cancelled (job))
1058
res = smbc_read (backend->smb_context, from_file,
1059
buffer, sizeof(buffer));
1060
if (res < 0 || g_vfs_job_is_cancelled (job))
1063
break; /* Succeeded */
1067
while (buffer_size > 0)
1069
res = smbc_write (backend->smb_context, to_file,
1071
if (res < 0 || g_vfs_job_is_cancelled (job))
1081
smbc_close (backend->smb_context, to_file);
1083
smbc_close (backend->smb_context, from_file);
1088
create_etag (struct stat *statbuf)
1090
return g_strdup_printf ("%lu", (long unsigned int)statbuf->st_mtime);
1094
do_replace (GVfsBackend *backend,
1095
GVfsJobOpenForWrite *job,
1096
const char *filename,
1098
gboolean make_backup,
1099
GFileCreateFlags flags)
1101
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1102
struct stat original_stat;
1104
char *uri, *tmp_uri, *backup_uri, *current_etag;
1106
GError *error = NULL;
1107
SmbWriteHandle *handle;
1108
smbc_open_fn smbc_open;
1109
smbc_stat_fn smbc_stat;
1111
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1114
backup_uri = g_strconcat (uri, "~", NULL);
1118
smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
1119
smbc_stat = smbc_getFunctionStat (op_backend->smb_context);
1122
file = smbc_open (op_backend->smb_context, uri,
1123
O_CREAT|O_WRONLY|O_EXCL, 0);
1124
if (file == NULL && errno != EEXIST)
1126
int errsv = fixup_open_errno (errno);
1128
g_set_error_literal (&error, G_IO_ERROR,
1129
g_io_error_from_errno (errsv),
1130
g_strerror (errsv));
1133
else if (file == NULL && errno == EEXIST)
1137
res = smbc_stat (op_backend->smb_context, uri, &original_stat);
1141
current_etag = create_etag (&original_stat);
1142
if (strcmp (etag, current_etag) != 0)
1144
g_free (current_etag);
1145
g_set_error_literal (&error,
1147
G_IO_ERROR_WRONG_ETAG,
1148
_("The file was externally modified"));
1151
g_free (current_etag);
1158
* 1) save to a tmp file (that doesn't exist already)
1159
* 2) rename orig file to backup file
1160
* (or delete it if no backup)
1161
* 3) rename tmp file to orig file
1163
* However, this can fail if we can't write to the directory.
1164
* In that case we just truncate the file, after having
1165
* copied directly to the backup filename.
1168
file = open_tmpfile (op_backend, uri, &tmp_uri);
1173
if (!copy_file (op_backend, G_VFS_JOB (job), uri, backup_uri))
1175
if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
1176
g_set_error_literal (&error,
1178
G_IO_ERROR_CANCELLED,
1179
_("Operation was cancelled"));
1181
g_set_error_literal (&error,
1183
G_IO_ERROR_CANT_CREATE_BACKUP,
1184
_("Backup file creation failed"));
1187
g_free (backup_uri);
1192
file = smbc_open (op_backend->smb_context, uri,
1193
O_CREAT|O_WRONLY|O_TRUNC, 0);
1196
int errsv = fixup_open_errno (errno);
1198
g_set_error_literal (&error, G_IO_ERROR,
1199
g_io_error_from_errno (errsv),
1200
g_strerror (errsv));
1207
/* Doesn't exist. Just write away */
1208
g_free (backup_uri);
1212
handle = g_new (SmbWriteHandle, 1);
1213
handle->file = file;
1215
handle->tmp_uri = tmp_uri;
1216
handle->backup_uri = backup_uri;
1218
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
1219
g_vfs_job_open_for_write_set_handle (job, handle);
1220
g_vfs_job_succeeded (G_VFS_JOB (job));
1225
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1226
g_error_free (error);
1227
g_free (backup_uri);
1234
do_write (GVfsBackend *backend,
1236
GVfsBackendHandle _handle,
1240
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1241
SmbWriteHandle *handle = _handle;
1243
smbc_write_fn smbc_write;
1245
smbc_write = smbc_getFunctionWrite (op_backend->smb_context);
1246
res = smbc_write (op_backend->smb_context, handle->file,
1247
buffer, buffer_size);
1249
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
1252
g_vfs_job_write_set_written_size (job, res);
1253
g_vfs_job_succeeded (G_VFS_JOB (job));
1258
do_seek_on_write (GVfsBackend *backend,
1259
GVfsJobSeekWrite *job,
1260
GVfsBackendHandle _handle,
1264
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1265
SmbWriteHandle *handle = _handle;
1268
smbc_lseek_fn smbc_lseek;
1282
g_vfs_job_failed (G_VFS_JOB (job),
1283
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1284
_("Unsupported seek type"));
1288
smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
1289
res = smbc_lseek (op_backend->smb_context, handle->file, offset, whence);
1291
if (res == (off_t)-1)
1292
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
1295
g_vfs_job_seek_write_set_offset (job, res);
1296
g_vfs_job_succeeded (G_VFS_JOB (job));
1303
do_query_info_on_write (GVfsBackend *backend,
1304
GVfsJobQueryInfoWrite *job,
1305
GVfsBackendHandle _handle,
1307
GFileAttributeMatcher *matcher)
1309
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1310
struct stat st = {0};
1311
SmbWriteHandle *handle = _handle;
1312
int res, saved_errno;
1313
smbc_fstat_fn smbc_fstat;
1315
smbc_fstat = smbc_getFunctionFstat (op_backend->smb_context);
1316
res = smbc_fstat (op_backend->smb_context, handle->file, &st);
1317
saved_errno = errno;
1321
set_info_from_stat (op_backend, info, &st, NULL, matcher);
1323
g_vfs_job_succeeded (G_VFS_JOB (job));
1326
g_vfs_job_failed_from_errno (G_VFS_JOB (job), saved_errno);
1331
do_close_write (GVfsBackend *backend,
1332
GVfsJobCloseWrite *job,
1333
GVfsBackendHandle _handle)
1335
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1336
SmbWriteHandle *handle = _handle;
1337
struct stat stat_at_close;
1340
smbc_fstat_fn smbc_fstat;
1341
smbc_close_fn smbc_close;
1342
smbc_unlink_fn smbc_unlink;
1343
smbc_rename_fn smbc_rename;
1345
smbc_fstat = smbc_getFunctionFstat (op_backend->smb_context);
1346
smbc_close = smbc_getFunctionClose (op_backend->smb_context);
1347
smbc_unlink = smbc_getFunctionUnlink (op_backend->smb_context);
1348
smbc_rename = smbc_getFunctionRename (op_backend->smb_context);
1350
stat_res = smbc_fstat (op_backend->smb_context, handle->file, &stat_at_close);
1352
res = smbc_close (op_backend->smb_context, handle->file);
1356
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
1358
if (handle->tmp_uri)
1359
smbc_unlink (op_backend->smb_context, handle->tmp_uri);
1363
if (handle->tmp_uri)
1365
if (handle->backup_uri)
1367
res = smbc_rename (op_backend->smb_context, handle->uri,
1368
op_backend->smb_context, handle->backup_uri);
1373
smbc_unlink (op_backend->smb_context, handle->tmp_uri);
1374
g_vfs_job_failed (G_VFS_JOB (job),
1375
G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
1376
_("Backup file creation failed: %s"), g_strerror (errsv));
1381
smbc_unlink (op_backend->smb_context, handle->uri);
1383
res = smbc_rename (op_backend->smb_context, handle->tmp_uri,
1384
op_backend->smb_context, handle->uri);
1387
smbc_unlink (op_backend->smb_context, handle->tmp_uri);
1388
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
1396
etag = create_etag (&stat_at_close);
1397
g_vfs_job_close_write_set_etag (job, etag);
1401
g_vfs_job_succeeded (G_VFS_JOB (job));
1404
smb_write_handle_free (handle);
1408
set_info_from_stat (GVfsBackendSmb *backend,
1410
struct stat *statbuf,
1411
const char *basename,
1412
GFileAttributeMatcher *matcher)
1414
GFileType file_type;
1422
g_file_info_set_name (info, basename);
1423
if (*basename == '.')
1424
g_file_info_set_is_hidden (info, TRUE);
1428
if (basename != NULL &&
1429
g_file_attribute_matcher_matches (matcher,
1430
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
1432
if (strcmp (basename, "/") == 0)
1433
display_name = g_strdup_printf (_("%s on %s"), backend->share, backend->server);
1435
display_name = g_filename_display_name (basename);
1437
if (strstr (display_name, "\357\277\275") != NULL)
1439
char *p = display_name;
1440
display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
1443
g_file_info_set_display_name (info, display_name);
1444
g_free (display_name);
1447
if (basename != NULL &&
1448
g_file_attribute_matcher_matches (matcher,
1449
G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME))
1451
char *edit_name = g_filename_display_name (basename);
1452
g_file_info_set_edit_name (info, edit_name);
1457
file_type = G_FILE_TYPE_UNKNOWN;
1459
if (S_ISREG (statbuf->st_mode))
1460
file_type = G_FILE_TYPE_REGULAR;
1461
else if (S_ISDIR (statbuf->st_mode))
1462
file_type = G_FILE_TYPE_DIRECTORY;
1463
else if (S_ISCHR (statbuf->st_mode) ||
1464
S_ISBLK (statbuf->st_mode) ||
1465
S_ISFIFO (statbuf->st_mode)
1467
|| S_ISSOCK (statbuf->st_mode)
1470
file_type = G_FILE_TYPE_SPECIAL;
1471
else if (S_ISLNK (statbuf->st_mode))
1472
file_type = G_FILE_TYPE_SYMBOLIC_LINK;
1474
g_file_info_set_file_type (info, file_type);
1475
g_file_info_set_size (info, statbuf->st_size);
1477
t.tv_sec = statbuf->st_mtime;
1478
#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
1479
t.tv_usec = statbuf->st_mtimensec / 1000;
1480
#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
1481
t.tv_usec = statbuf->st_mtim.tv_nsec / 1000;
1485
g_file_info_set_modification_time (info, &t);
1488
if (g_file_attribute_matcher_matches (matcher,
1489
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE) ||
1490
g_file_attribute_matcher_matches (matcher,
1491
G_FILE_ATTRIBUTE_STANDARD_ICON))
1494
content_type = NULL;
1496
if (S_ISDIR(statbuf->st_mode))
1498
content_type = g_strdup ("inode/directory");
1499
if (basename != NULL && strcmp (basename, "/") == 0)
1500
icon = g_themed_icon_new ("folder-remote");
1502
icon = g_themed_icon_new ("folder");
1504
else if (basename != NULL)
1506
content_type = g_content_type_guess (basename, NULL, 0, NULL);
1508
icon = g_content_type_get_icon (content_type);
1513
g_file_info_set_content_type (info, content_type);
1514
g_free (content_type);
1518
icon = g_themed_icon_new ("text-x-generic");
1520
g_file_info_set_icon (info, icon);
1521
g_object_unref (icon);
1524
/* Don't trust n_link, uid, gid, etc returned from libsmb, its just made up.
1525
These are ok though: */
1527
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE, statbuf->st_dev);
1528
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE, statbuf->st_ino);
1530
/* If file is dos-readonly, libsmbclient doesn't set S_IWUSR, we use this to
1531
trigger ACCESS_WRITE = FALSE: */
1532
if (!(statbuf->st_mode & S_IWUSR))
1533
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
1535
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS, statbuf->st_atime);
1536
#if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
1537
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000);
1538
#elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
1539
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000);
1541
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED, statbuf->st_ctime);
1542
#if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC)
1543
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000);
1544
#elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC)
1545
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, statbuf->st_ctim.tv_nsec / 1000);
1548
/* Libsmb sets the X bit on files to indicate some special things: */
1549
if ((statbuf->st_mode & S_IFDIR) == 0) {
1551
if (statbuf->st_mode & S_IXOTH)
1552
g_file_info_set_is_hidden (info, TRUE);
1554
if (statbuf->st_mode & S_IXUSR)
1555
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE, TRUE);
1557
if (statbuf->st_mode & S_IXGRP)
1558
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_DOS_IS_SYSTEM, TRUE);
1561
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_ETAG_VALUE))
1563
char *etag = create_etag (statbuf);
1564
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
1570
do_query_info (GVfsBackend *backend,
1571
GVfsJobQueryInfo *job,
1572
const char *filename,
1573
GFileQueryInfoFlags flags,
1575
GFileAttributeMatcher *matcher)
1577
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1578
struct stat st = {0};
1580
int res, saved_errno;
1582
smbc_stat_fn smbc_stat;
1584
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1585
smbc_stat = smbc_getFunctionStat (op_backend->smb_context);
1586
res = smbc_stat (op_backend->smb_context, uri, &st);
1587
saved_errno = errno;
1592
basename = g_path_get_basename (filename);
1593
set_info_from_stat (op_backend, info, &st, basename, matcher);
1596
g_vfs_job_succeeded (G_VFS_JOB (job));
1599
g_vfs_job_failed_from_errno (G_VFS_JOB (job), saved_errno);
1604
do_query_fs_info (GVfsBackend *backend,
1605
GVfsJobQueryFsInfo *job,
1606
const char *filename,
1608
GFileAttributeMatcher *attribute_matcher)
1610
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1612
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs");
1614
#ifdef HAVE_SAMBA_STAT_VFS
1615
smbc_statvfs_fn smbc_statvfs;
1616
struct statvfs st = {0};
1620
if (g_file_attribute_matcher_matches (attribute_matcher,
1621
G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||
1622
g_file_attribute_matcher_matches (attribute_matcher,
1623
G_FILE_ATTRIBUTE_FILESYSTEM_FREE) ||
1624
g_file_attribute_matcher_matches (attribute_matcher,
1625
G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
1627
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1628
smbc_statvfs = smbc_getFunctionStatVFS (op_backend->smb_context);
1629
res = smbc_statvfs (op_backend->smb_context, uri, &st);
1634
/* older samba versions ( < 3.0.28) return zero values in struct statvfs */
1635
if (st.f_blocks > 0)
1637
/* FIXME: inconsistent return values (libsmbclient-3.4.2)
1638
* - for linux samba hosts, f_frsize is zero and f_bsize is a real block size
1639
* - for some Windows hosts (XP), f_frsize and f_bsize should be multiplied to get real block size
1641
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, st.f_bsize * st.f_blocks * ((st.f_frsize == 0) ? 1 : st.f_frsize));
1642
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, st.f_bsize * st.f_bfree * ((st.f_frsize == 0) ? 1 : st.f_frsize));
1643
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, st.f_flag & SMBC_VFS_FEATURE_RDONLY);
1649
g_vfs_job_succeeded (G_VFS_JOB (job));
1654
try_query_settable_attributes (GVfsBackend *backend,
1655
GVfsJobQueryAttributes *job,
1656
const char *filename)
1658
GFileAttributeInfoList *list;
1660
list = g_file_attribute_info_list_new ();
1662
/* TODO: Add all settable attributes here -- bug #559586 */
1663
/* TODO: xattrs support? */
1665
g_file_attribute_info_list_add (list,
1666
G_FILE_ATTRIBUTE_TIME_MODIFIED,
1667
G_FILE_ATTRIBUTE_TYPE_UINT64,
1668
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
1669
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
1672
/* FIXME: disabled; despite chmod is supported, it makes no sense on samba shares and
1673
libsmbclient lacks proper API to read unix file modes.
1674
The struct stat->st_mode member is used for special Windows attributes. */
1675
g_file_attribute_info_list_add (list,
1676
G_FILE_ATTRIBUTE_UNIX_MODE,
1677
G_FILE_ATTRIBUTE_TYPE_UINT32,
1678
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
1679
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
1682
g_vfs_job_query_attributes_set_list (job, list);
1683
g_vfs_job_succeeded (G_VFS_JOB (job));
1685
g_file_attribute_info_list_unref (list);
1690
do_set_attribute (GVfsBackend *backend,
1691
GVfsJobSetAttribute *job,
1692
const char *filename,
1693
const char *attribute,
1694
GFileAttributeType type,
1696
GFileQueryInfoFlags flags)
1698
GVfsBackendSmb *op_backend;
1701
struct timeval tbuf[2];
1702
smbc_utimes_fn smbc_utimes;
1704
smbc_chmod_fn smbc_chmod;
1708
op_backend = G_VFS_BACKEND_SMB (backend);
1710
if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) != 0
1712
&& strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) != 0
1716
g_vfs_job_failed (G_VFS_JOB (job),
1717
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1718
_("Operation unsupported"));
1722
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1725
if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
1727
if (type != G_FILE_ATTRIBUTE_TYPE_UINT64)
1729
g_vfs_job_failed (G_VFS_JOB (job),
1731
G_IO_ERROR_INVALID_ARGUMENT,
1733
_("Invalid attribute type (uint64 expected)"));
1736
smbc_utimes = smbc_getFunctionUtimes (op_backend->smb_context);
1737
tbuf[1].tv_sec = (*(guint64 *)value_p); /* mtime */
1738
tbuf[1].tv_usec = 0;
1739
/* atime = mtime (atimes are usually disabled on desktop systems) */
1740
tbuf[0].tv_sec = tbuf[1].tv_sec;
1741
tbuf[0].tv_usec = 0;
1742
res = smbc_utimes (op_backend->smb_context, uri, &tbuf[0]);
1746
if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
1748
smbc_chmod = smbc_getFunctionChmod (op_backend->smb_context);
1749
res = smbc_chmod (op_backend->smb_context, uri, (*(guint32 *)value_p) & 0777);
1757
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
1759
g_vfs_job_succeeded (G_VFS_JOB (job));
1763
do_enumerate (GVfsBackend *backend,
1764
GVfsJobEnumerate *job,
1765
const char *filename,
1766
GFileAttributeMatcher *matcher,
1767
GFileQueryInfoFlags flags)
1769
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1774
char dirents[1024*4];
1775
struct smbc_dirent *dirp;
1780
smbc_opendir_fn smbc_opendir;
1781
smbc_getdents_fn smbc_getdents;
1782
smbc_stat_fn smbc_stat;
1783
smbc_closedir_fn smbc_closedir;
1785
uri = create_smb_uri_string (op_backend->server, op_backend->share, filename);
1787
smbc_opendir = smbc_getFunctionOpendir (op_backend->smb_context);
1788
smbc_getdents = smbc_getFunctionGetdents (op_backend->smb_context);
1789
smbc_stat = smbc_getFunctionStat (op_backend->smb_context);
1790
smbc_closedir = smbc_getFunctionClosedir (op_backend->smb_context);
1792
dir = smbc_opendir (op_backend->smb_context, uri->str);
1799
g_set_error_literal (&error, G_IO_ERROR,
1800
g_io_error_from_errno (errsv),
1801
g_strerror (errsv));
1805
g_vfs_job_succeeded (G_VFS_JOB (job));
1807
if (uri->str[uri->len - 1] != '/')
1808
g_string_append_c (uri, '/');
1809
uri_start_len = uri->len;
1815
res = smbc_getdents (op_backend->smb_context, dir, (struct smbc_dirent *)dirents, sizeof (dirents));
1819
dirp = (struct smbc_dirent *)dirents;
1822
unsigned int dirlen;
1824
/* TODO: Only do stat if required for flags */
1826
if ((dirp->smbc_type == SMBC_DIR ||
1827
dirp->smbc_type == SMBC_FILE ||
1828
dirp->smbc_type == SMBC_LINK) &&
1829
strcmp (dirp->name, ".") != 0 &&
1830
strcmp (dirp->name, "..") != 0)
1833
g_string_truncate (uri, uri_start_len);
1834
g_string_append_encoded (uri,
1836
SUB_DELIM_CHARS ":@/");
1838
if (matcher == NULL ||
1839
g_file_attribute_matcher_matches_only (matcher, G_FILE_ATTRIBUTE_STANDARD_NAME))
1841
info = g_file_info_new ();
1842
g_file_info_set_name (info, dirp->name);
1843
files = g_list_prepend (files, info);
1847
stat_res = smbc_stat (op_backend->smb_context,
1851
info = g_file_info_new ();
1852
set_info_from_stat (op_backend, info, &st, dirp->name, matcher);
1853
files = g_list_prepend (files, info);
1858
dirlen = dirp->dirlen;
1859
dirp = (struct smbc_dirent *) (((char *)dirp) + dirlen);
1865
files = g_list_reverse (files);
1866
g_vfs_job_enumerate_add_infos (job, files);
1867
g_list_foreach (files, (GFunc)g_object_unref, NULL);
1868
g_list_free (files);
1872
res = smbc_closedir (op_backend->smb_context, dir);
1874
g_vfs_job_enumerate_done (job);
1876
g_string_free (uri, TRUE);
1880
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1881
g_error_free (error);
1882
g_string_free (uri, TRUE);
1886
do_set_display_name (GVfsBackend *backend,
1887
GVfsJobSetDisplayName *job,
1888
const char *filename,
1889
const char *display_name)
1891
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1892
char *from_uri, *to_uri;
1893
char *dirname, *new_path;
1895
smbc_rename_fn smbc_rename;
1897
dirname = g_path_get_dirname (filename);
1899
/* TODO: display name is in utf8, atm we assume libsmb uris
1900
are in utf8, but this might not be true if the user changed
1901
the smb.conf file. Can we check this and convert? */
1903
new_path = g_build_filename (dirname, display_name, NULL);
1906
from_uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1907
to_uri = create_smb_uri (op_backend->server, op_backend->share, new_path);
1909
smbc_rename = smbc_getFunctionRename (op_backend->smb_context);
1910
res = smbc_rename (op_backend->smb_context, from_uri,
1911
op_backend->smb_context, to_uri);
1917
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
1920
g_vfs_job_set_display_name_set_new_path (job, new_path);
1921
g_vfs_job_succeeded (G_VFS_JOB (job));
1927
do_delete (GVfsBackend *backend,
1929
const char *filename)
1931
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1932
struct stat statbuf;
1935
smbc_stat_fn smbc_stat;
1936
smbc_rmdir_fn smbc_rmdir;
1937
smbc_unlink_fn smbc_unlink;
1940
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1942
smbc_stat = smbc_getFunctionStat (op_backend->smb_context);
1943
smbc_rmdir = smbc_getFunctionRmdir (op_backend->smb_context);
1944
smbc_unlink = smbc_getFunctionUnlink (op_backend->smb_context);
1946
res = smbc_stat (op_backend->smb_context, uri, &statbuf);
1951
g_vfs_job_failed (G_VFS_JOB (job),
1953
g_io_error_from_errno (errsv),
1954
_("Error deleting file: %s"),
1955
g_strerror (errsv));
1960
if (S_ISDIR (statbuf.st_mode))
1961
res = smbc_rmdir (op_backend->smb_context, uri);
1963
res = smbc_unlink (op_backend->smb_context, uri);
1968
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
1970
g_vfs_job_succeeded (G_VFS_JOB (job));
1974
do_make_directory (GVfsBackend *backend,
1975
GVfsJobMakeDirectory *job,
1976
const char *filename)
1978
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1981
smbc_mkdir_fn smbc_mkdir;
1983
uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1984
smbc_mkdir = smbc_getFunctionMkdir (op_backend->smb_context);
1985
res = smbc_mkdir (op_backend->smb_context, uri, 0666);
1990
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
1992
g_vfs_job_succeeded (G_VFS_JOB (job));
1996
do_move (GVfsBackend *backend,
1999
const char *destination,
2000
GFileCopyFlags flags,
2001
GFileProgressCallback progress_callback,
2002
gpointer progress_callback_data)
2004
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
2005
char *source_uri, *dest_uri, *backup_uri;
2006
gboolean destination_exist, source_is_dir;
2007
struct stat statbuf;
2009
smbc_stat_fn smbc_stat;
2010
smbc_rename_fn smbc_rename;
2011
smbc_unlink_fn smbc_unlink;
2014
source_uri = create_smb_uri (op_backend->server, op_backend->share, source);
2016
smbc_stat = smbc_getFunctionStat (op_backend->smb_context);
2017
smbc_rename = smbc_getFunctionRename (op_backend->smb_context);
2018
smbc_unlink = smbc_getFunctionUnlink (op_backend->smb_context);
2020
res = smbc_stat (op_backend->smb_context, source_uri, &statbuf);
2025
g_vfs_job_failed (G_VFS_JOB (job),
2027
g_io_error_from_errno (errsv),
2028
_("Error moving file: %s"),
2029
g_strerror (errsv));
2030
g_free (source_uri);
2034
source_is_dir = S_ISDIR (statbuf.st_mode);
2036
dest_uri = create_smb_uri (op_backend->server, op_backend->share, destination);
2038
destination_exist = FALSE;
2039
res = smbc_stat (op_backend->smb_context, dest_uri, &statbuf);
2042
destination_exist = TRUE; /* Target file exists */
2044
if (flags & G_FILE_COPY_OVERWRITE)
2046
/* Always fail on dirs, even with overwrite */
2047
if (S_ISDIR (statbuf.st_mode))
2049
g_vfs_job_failed (G_VFS_JOB (job),
2051
G_IO_ERROR_WOULD_MERGE,
2052
_("Can't move directory over directory"));
2053
g_free (source_uri);
2060
g_vfs_job_failed (G_VFS_JOB (job),
2063
_("Target file already exists"));
2064
g_free (source_uri);
2070
if (flags & G_FILE_COPY_BACKUP && destination_exist)
2072
backup_uri = g_strconcat (dest_uri, "~", NULL);
2073
res = smbc_rename (op_backend->smb_context, dest_uri,
2074
op_backend->smb_context, backup_uri);
2077
g_vfs_job_failed (G_VFS_JOB (job),
2079
G_IO_ERROR_CANT_CREATE_BACKUP,
2080
_("Backup file creation failed"));
2081
g_free (source_uri);
2083
g_free (backup_uri);
2086
g_free (backup_uri);
2087
destination_exist = FALSE; /* It did, but no more */
2090
if (source_is_dir && destination_exist && (flags & G_FILE_COPY_OVERWRITE))
2092
/* Source is a dir, destination exists (and is not a dir, because that would have failed
2093
earlier), and we're overwriting. Manually remove the target so we can do the rename. */
2094
res = smbc_unlink (op_backend->smb_context, dest_uri);
2098
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2099
g_io_error_from_errno (errsv),
2100
_("Error removing target file: %s"),
2101
g_strerror (errsv));
2102
g_free (source_uri);
2109
res = smbc_rename (op_backend->smb_context, source_uri,
2110
op_backend->smb_context, dest_uri);
2112
g_free (source_uri);
2115
/* Catch moves across device boundaries */
2118
if (errsv == EXDEV ||
2119
/* Unfortunately libsmbclient doesn't correctly return EXDEV, but falls back
2120
to EINVAL, so we try to guess when this happens: */
2121
(errsv == EINVAL && source_is_dir))
2122
g_vfs_job_failed (G_VFS_JOB (job),
2123
G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
2124
_("Can't recursively move directory"));
2126
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
2129
g_vfs_job_succeeded (G_VFS_JOB (job));
2133
g_vfs_backend_smb_class_init (GVfsBackendSmbClass *klass)
2135
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2136
GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
2138
gobject_class->finalize = g_vfs_backend_smb_finalize;
2140
backend_class->mount = do_mount;
2141
backend_class->try_mount = try_mount;
2142
backend_class->open_for_read = do_open_for_read;
2143
backend_class->read = do_read;
2144
backend_class->seek_on_read = do_seek_on_read;
2145
backend_class->query_info_on_read = do_query_info_on_read;
2146
backend_class->close_read = do_close_read;
2147
backend_class->create = do_create;
2148
backend_class->append_to = do_append_to;
2149
backend_class->replace = do_replace;
2150
backend_class->write = do_write;
2151
backend_class->seek_on_write = do_seek_on_write;
2152
backend_class->query_info_on_write = do_query_info_on_write;
2153
backend_class->close_write = do_close_write;
2154
backend_class->query_info = do_query_info;
2155
backend_class->query_fs_info = do_query_fs_info;
2156
backend_class->enumerate = do_enumerate;
2157
backend_class->set_display_name = do_set_display_name;
2158
backend_class->delete = do_delete;
2159
backend_class->make_directory = do_make_directory;
2160
backend_class->move = do_move;
2161
backend_class->try_query_settable_attributes = try_query_settable_attributes;
2162
backend_class->set_attribute = do_set_attribute;
2166
g_vfs_smb_daemon_init (void)
2168
g_set_application_name (_("Windows Shares Filesystem Service"));