~ubuntu-branches/ubuntu/natty/gvfs/natty

« back to all changes in this revision

Viewing changes to .pc/debian-changes-1.7.3-0ubuntu1/daemon/gvfsbackendsmb.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2011-03-22 11:11:57 UTC
  • Revision ID: james.westby@ubuntu.com-20110322111157-tn8edztqci0xr9hk
Correctly update using merge-upstream otherwise the diff will be reverted
in the diff.gz.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* GIO - GLib Input, Output and Streaming Library
2
 
 * 
3
 
 * Copyright (C) 2006-2007 Red Hat, Inc.
4
 
 *
5
 
 * This library is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU Lesser General Public
7
 
 * License as published by the Free Software Foundation; either
8
 
 * version 2 of the License, or (at your option) any later version.
9
 
 *
10
 
 * This library is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
 * Lesser General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU Lesser General
16
 
 * Public License along with this library; if not, write to the
17
 
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18
 
 * Boston, MA 02111-1307, USA.
19
 
 *
20
 
 * Author: Alexander Larsson <alexl@redhat.com>
21
 
 */
22
 
 
23
 
#include <config.h>
24
 
 
25
 
#include <sys/types.h>
26
 
#include <sys/stat.h>
27
 
#include <errno.h>
28
 
#include <unistd.h>
29
 
#include <fcntl.h>
30
 
#include <string.h>
31
 
#include <stdlib.h>
32
 
 
33
 
#include <glib/gstdio.h>
34
 
#include <glib/gi18n.h>
35
 
#include <gio/gio.h>
36
 
 
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"
52
 
 
53
 
#include <libsmbclient.h>
54
 
#include "libsmb-compat.h"
55
 
 
56
 
struct _GVfsBackendSmb
57
 
{
58
 
  GVfsBackend parent_instance;
59
 
 
60
 
  char *server;
61
 
  char *share;
62
 
  char *user;
63
 
  char *domain;
64
 
  char *path;
65
 
  char *default_workgroup;
66
 
  
67
 
  SMBCCTX *smb_context;
68
 
 
69
 
  char *last_user;
70
 
  char *last_domain;
71
 
  char *last_password;
72
 
  
73
 
  GMountSource *mount_source; /* Only used/set during mount */
74
 
  int mount_try;
75
 
  gboolean mount_try_again;
76
 
  gboolean mount_cancelled;
77
 
        
78
 
  gboolean password_in_keyring;
79
 
  GPasswordSave password_save;
80
 
  
81
 
  /* Cache */
82
 
  char *cached_server_name;
83
 
  char *cached_share_name;
84
 
  char *cached_domain;
85
 
  char *cached_username;
86
 
  SMBCSRV *cached_server;
87
 
};
88
 
 
89
 
 
90
 
G_DEFINE_TYPE (GVfsBackendSmb, g_vfs_backend_smb, G_VFS_TYPE_BACKEND)
91
 
 
92
 
static void set_info_from_stat (GVfsBackendSmb *backend,
93
 
                                GFileInfo *info,
94
 
                                struct stat *statbuf,
95
 
                                const char *basename,
96
 
                                GFileAttributeMatcher *matcher);
97
 
 
98
 
 
99
 
static void
100
 
g_vfs_backend_smb_finalize (GObject *object)
101
 
{
102
 
  GVfsBackendSmb *backend;
103
 
 
104
 
  backend = G_VFS_BACKEND_SMB (object);
105
 
 
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);
112
 
  
113
 
  if (G_OBJECT_CLASS (g_vfs_backend_smb_parent_class)->finalize)
114
 
    (*G_OBJECT_CLASS (g_vfs_backend_smb_parent_class)->finalize) (object);
115
 
}
116
 
 
117
 
static void
118
 
g_vfs_backend_smb_init (GVfsBackendSmb *backend)
119
 
{
120
 
  char *workgroup;
121
 
  GSettings *settings;
122
 
 
123
 
  /* Get default workgroup name */
124
 
  settings = g_settings_new ("org.gnome.system.smb");
125
 
 
126
 
  workgroup = g_settings_get_string (settings, "workgroup");
127
 
  if (workgroup && workgroup[0])
128
 
    backend->default_workgroup = workgroup;
129
 
  else
130
 
    g_free (workgroup);
131
 
 
132
 
  g_object_unref (settings);
133
 
}
134
 
 
135
 
/**
136
 
 * Authentication callback function type (method that includes context)
137
 
 * 
138
 
 * Type for the the authentication function called by the library to
139
 
 * obtain authentication credentals
140
 
 *
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
151
 
 *                  wrong.
152
 
 * @param unlen     The size of the username buffer in bytes
153
 
 * @param pw        Pointer to buffer containing to which password 
154
 
 *                  copied
155
 
 * @param pwlen     The size of the password buffer in bytes
156
 
 *           
157
 
 */
158
 
static void
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)
164
 
{
165
 
  GVfsBackendSmb *backend;
166
 
  char *ask_password, *ask_user, *ask_domain;
167
 
  gboolean handled, abort;
168
 
 
169
 
  backend = smbc_getOptionUserData (context);
170
 
 
171
 
  strncpy (password_out, "", pwmaxlen);
172
 
  
173
 
  if (backend->domain)
174
 
    strncpy (domain_out, backend->domain, domainmaxlen);
175
 
  if (backend->user)
176
 
    strncpy (username_out, backend->user, unmaxlen);
177
 
 
178
 
  if (backend->mount_cancelled)
179
 
    {
180
 
      /*  Don't prompt for credentials, let smbclient finish the mount loop  */
181
 
      strncpy (username_out, "ABORT", unmaxlen);
182
 
      strncpy (password_out, "", pwmaxlen);
183
 
      return;
184
 
    }
185
 
 
186
 
  if (backend->mount_source == NULL)
187
 
    {
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);
195
 
      
196
 
      return;
197
 
    }
198
 
  
199
 
  if (backend->mount_try == 0 &&
200
 
      backend->user == NULL &&
201
 
      backend->domain == NULL)
202
 
    {
203
 
      /* Try again if kerberos login + anonymous fallback fails */
204
 
      backend->mount_try_again = TRUE;
205
 
    }
206
 
  else
207
 
    {
208
 
      gboolean in_keyring = FALSE;
209
 
 
210
 
      if (!backend->password_in_keyring)
211
 
        {
212
 
          in_keyring = g_vfs_keyring_lookup_password (backend->user,
213
 
                                                      backend->server,
214
 
                                                      backend->domain,
215
 
                                                      "smb",
216
 
                                                      NULL,
217
 
                                                      NULL,
218
 
                                                      0,
219
 
                                                      &ask_user,
220
 
                                                      &ask_domain,
221
 
                                                      &ask_password);
222
 
          backend->password_in_keyring = in_keyring;
223
 
        }
224
 
      
225
 
      if (!in_keyring)
226
 
        {
227
 
          GAskPasswordFlags flags = G_ASK_PASSWORD_NEED_PASSWORD;
228
 
          char *message;
229
 
      
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;
236
 
 
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,
241
 
                                                 message,
242
 
                                                 username_out,
243
 
                                                 domain_out,
244
 
                                                 flags,
245
 
                                                 &abort,
246
 
                                                 &ask_password,
247
 
                                                 &ask_user,
248
 
                                                 &ask_domain,
249
 
                                                 NULL,
250
 
                                                 &(backend->password_save));
251
 
          g_free (message);
252
 
          if (!handled)
253
 
            goto out;
254
 
      
255
 
          if (abort)
256
 
            {
257
 
              strncpy (username_out, "ABORT", unmaxlen);
258
 
              strncpy (password_out, "", pwmaxlen);
259
 
              backend->mount_cancelled = TRUE;
260
 
              goto out;
261
 
            }
262
 
        }
263
 
 
264
 
      /* Try again if this fails */
265
 
      backend->mount_try_again = TRUE;
266
 
 
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);
272
 
 
273
 
    out:
274
 
      g_free (ask_password);
275
 
      g_free (ask_user);
276
 
      g_free (ask_domain);
277
 
    }
278
 
 
279
 
  backend->last_user = g_strdup (username_out);
280
 
  backend->last_domain = g_strdup (domain_out);
281
 
  backend->last_password = g_strdup (password_out);
282
 
}
283
 
 
284
 
/* Add a server to the cache system
285
 
 *
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.
293
 
 *
294
 
 */ 
295
 
static int
296
 
add_cached_server (SMBCCTX *context, SMBCSRV *new,
297
 
                   const char *server_name, const char *share_name, 
298
 
                   const char *domain, const char *username)
299
 
{
300
 
  GVfsBackendSmb *backend;
301
 
 
302
 
  backend = smbc_getOptionUserData (context);
303
 
  
304
 
  if (backend->cached_server != NULL)
305
 
    return 1;
306
 
 
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;
312
 
 
313
 
  return 0;
314
 
}
315
 
 
316
 
/* Remove cached server
317
 
 *
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.
321
 
 *
322
 
 */ 
323
 
static int
324
 
remove_cached_server(SMBCCTX * context, SMBCSRV * server)
325
 
{
326
 
  GVfsBackendSmb *backend;
327
 
 
328
 
  backend = smbc_getOptionUserData (context);
329
 
  
330
 
  if (backend->cached_server == server)
331
 
    {
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;
341
 
      return 0;
342
 
    }
343
 
  return 1;
344
 
}
345
 
 
346
 
 
347
 
/* Look up a server in the cache system
348
 
 *
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.
355
 
 *
356
 
 */ 
357
 
static SMBCSRV *
358
 
get_cached_server (SMBCCTX * context,
359
 
                   const char *server_name, const char *share_name,
360
 
                   const char *domain, const char *username)
361
 
{
362
 
  GVfsBackendSmb *backend;
363
 
 
364
 
  backend = smbc_getOptionUserData (context);
365
 
 
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;
372
 
 
373
 
  return NULL;
374
 
}
375
 
 
376
 
/* Try to remove all servers from the cache system and disconnect
377
 
 *
378
 
 * @param c         pointer to smb context
379
 
 *
380
 
 * @return          0 when found and removed. 1 on failure.
381
 
 *
382
 
 */ 
383
 
static int
384
 
purge_cached (SMBCCTX * context)
385
 
{
386
 
  GVfsBackendSmb *backend;
387
 
  
388
 
  backend = smbc_getOptionUserData (context);
389
 
 
390
 
  if (backend->cached_server)
391
 
    remove_cached_server(context, backend->cached_server);
392
 
  
393
 
  return 0;
394
 
}
395
 
 
396
 
#define SUB_DELIM_CHARS  "!$&'()*+,;="
397
 
 
398
 
static gboolean
399
 
is_valid (char c, const char *reserved_chars_allowed)
400
 
{
401
 
  if (g_ascii_isalnum (c) ||
402
 
      c == '-' ||
403
 
      c == '.' ||
404
 
      c == '_' ||
405
 
      c == '~')
406
 
    return TRUE;
407
 
 
408
 
  if (reserved_chars_allowed &&
409
 
      strchr (reserved_chars_allowed, c) != NULL)
410
 
    return TRUE;
411
 
  
412
 
  return FALSE;
413
 
}
414
 
 
415
 
static void
416
 
g_string_append_encoded (GString *string,
417
 
                         const char *encoded,
418
 
                         const char *reserved_chars_allowed)
419
 
{
420
 
  char c;
421
 
  static const gchar hex[16] = "0123456789ABCDEF";
422
 
  
423
 
  while ((c = *encoded++) != 0)
424
 
    {
425
 
      if (is_valid (c, reserved_chars_allowed))
426
 
        g_string_append_c (string, c);
427
 
      else
428
 
        {
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]);
432
 
        }
433
 
    }
434
 
}
435
 
 
436
 
static GString *
437
 
create_smb_uri_string (const char *server,
438
 
                       const char *share,
439
 
                       const char *path)
440
 
{
441
 
  GString *uri;
442
 
 
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);
447
 
  if (path != NULL)
448
 
    {
449
 
      if (*path != '/')
450
 
        g_string_append_c (uri, '/');
451
 
      g_string_append_encoded (uri, path, SUB_DELIM_CHARS ":@/");
452
 
    }
453
 
 
454
 
  while (uri->len > 0 &&
455
 
         uri->str[uri->len - 1] == '/')
456
 
    g_string_erase (uri, uri->len - 1, 1);
457
 
  
458
 
  return uri;
459
 
}
460
 
 
461
 
static char *
462
 
create_smb_uri (const char *server,
463
 
                const char *share,
464
 
                const char *path)
465
 
{
466
 
  GString *uri;
467
 
  uri = create_smb_uri_string (server, share, path);
468
 
  return g_string_free (uri, FALSE);
469
 
}
470
 
 
471
 
static void
472
 
do_mount (GVfsBackend *backend,
473
 
          GVfsJobMount *job,
474
 
          GMountSpec *mount_spec,
475
 
          GMountSource *mount_source,
476
 
          gboolean is_automount)
477
 
{
478
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
479
 
  SMBCCTX *smb_context;
480
 
  struct stat st;
481
 
  char *uri;
482
 
  int res;
483
 
  char *display_name;
484
 
  const char *debug;
485
 
  int debug_val;
486
 
  GMountSpec *smb_mount_spec;
487
 
  smbc_stat_fn smbc_stat;
488
 
 
489
 
  smb_context = smbc_new_context ();
490
 
  if (smb_context == NULL)
491
 
    {
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");
495
 
      return;
496
 
    }
497
 
  smbc_setOptionUserData (smb_context, backend);
498
 
 
499
 
  debug = g_getenv ("GVFS_SMB_DEBUG");
500
 
  if (debug)
501
 
    debug_val = atoi (debug);
502
 
  else
503
 
    debug_val = 0;
504
 
 
505
 
  smbc_setDebug (smb_context, debug_val);
506
 
  smbc_setFunctionAuthDataWithContext (smb_context, auth_callback);
507
 
  
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);
512
 
 
513
 
  /* FIXME: is strdup() still needed here? -- removed */
514
 
  if (op_backend->default_workgroup != NULL)
515
 
    smbc_setWorkgroup (smb_context, op_backend->default_workgroup);
516
 
 
517
 
#ifndef DEPRECATED_SMBC_INTERFACE
518
 
  smb_context->flags = 0;
519
 
#endif
520
 
  
521
 
  /* Initial settings:
522
 
   *   - use Kerberos (always)
523
 
   *   - in case of no username specified, try anonymous login
524
 
   */
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);
530
 
 
531
 
  
532
 
#if 0
533
 
  smbc_setOptionDebugToStderr (smb_context, 1);
534
 
#endif
535
 
  
536
 
  if (!smbc_init_context (smb_context))
537
 
    {
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);
542
 
      return;
543
 
    }
544
 
 
545
 
  op_backend->smb_context = smb_context;
546
 
 
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
551
 
   */
552
 
 
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");
558
 
 
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);
566
 
 
567
 
  g_vfs_backend_set_mount_spec (backend, smb_mount_spec);
568
 
  g_mount_spec_unref (smb_mount_spec);
569
 
 
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);
575
 
 
576
 
 
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;
581
 
 
582
 
  do
583
 
    {
584
 
      op_backend->mount_try_again = FALSE;
585
 
      op_backend->mount_cancelled = FALSE;
586
 
 
587
 
      smbc_stat = smbc_getFunctionStat (smb_context);
588
 
      res = smbc_stat (smb_context, uri, &st);
589
 
      
590
 
      if (res == 0 || op_backend->mount_cancelled ||
591
 
          (errno != EACCES && errno != EPERM))
592
 
        break;
593
 
 
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).
597
 
       */
598
 
      if (op_backend->mount_try == 0)
599
 
        {
600
 
          smbc_setOptionFallbackAfterKerberos (op_backend->smb_context, 1);
601
 
          smbc_setOptionNoAutoAnonymousLogin (op_backend->smb_context, 1);
602
 
        }
603
 
      op_backend->mount_try ++;
604
 
    }
605
 
  while (op_backend->mount_try_again);
606
 
  
607
 
  g_free (uri);
608
 
  
609
 
  op_backend->mount_source = NULL;
610
 
 
611
 
  if (res != 0)
612
 
    {
613
 
      /* TODO: Error from errno? */
614
 
      op_backend->mount_source = NULL;
615
 
      
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"));
620
 
      else
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"));
625
 
 
626
 
      return;
627
 
    }
628
 
 
629
 
  /* Mount was successful */
630
 
 
631
 
  g_vfs_backend_set_default_location (backend, op_backend->path);
632
 
  g_vfs_keyring_save_password (op_backend->last_user,
633
 
                               op_backend->server,
634
 
                               op_backend->last_domain,
635
 
                               "smb",
636
 
                               NULL,
637
 
                               NULL,
638
 
                               0,
639
 
                               op_backend->last_password,
640
 
                               op_backend->password_save);
641
 
  
642
 
  g_vfs_job_succeeded (G_VFS_JOB (job));
643
 
}
644
 
 
645
 
static gboolean
646
 
try_mount (GVfsBackend *backend,
647
 
           GVfsJobMount *job,
648
 
           GMountSpec *mount_spec,
649
 
           GMountSource *mount_source,
650
 
           gboolean is_automount)
651
 
{
652
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
653
 
  const char *server, *share, *user, *domain, *path;
654
 
 
655
 
  server = g_mount_spec_get (mount_spec, "server");
656
 
  share = g_mount_spec_get (mount_spec, "share");
657
 
 
658
 
  if (server == NULL || share == NULL)
659
 
    {
660
 
      g_vfs_job_failed (G_VFS_JOB (job),
661
 
                        G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
662
 
                        _("Invalid mount spec"));
663
 
      return TRUE;
664
 
    }
665
 
 
666
 
  user = g_mount_spec_get (mount_spec, "user");
667
 
  domain = g_mount_spec_get (mount_spec, "domain");
668
 
  path = mount_spec->mount_prefix;
669
 
  
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);
675
 
  
676
 
  return FALSE;
677
 
}
678
 
 
679
 
static int
680
 
fixup_open_errno (int err)
681
 
{
682
 
  /* samba has a bug (#6228) where it doesn't set errno if path resolving failed */
683
 
  if (err == 0)
684
 
    err = ENOTDIR;
685
 
  return err;
686
 
}
687
 
 
688
 
static void 
689
 
do_open_for_read (GVfsBackend *backend,
690
 
                  GVfsJobOpenForRead *job,
691
 
                  const char *filename)
692
 
{
693
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
694
 
  char *uri;
695
 
  SMBCFILE *file;
696
 
  struct stat st;
697
 
  smbc_open_fn smbc_open;
698
 
  smbc_stat_fn smbc_stat;
699
 
  int res;
700
 
  int olderr;
701
 
 
702
 
 
703
 
  uri = create_smb_uri (op_backend->server, op_backend->share, filename);
704
 
  smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
705
 
  errno = 0;
706
 
  file = smbc_open (op_backend->smb_context, uri, O_RDONLY, 0);
707
 
 
708
 
  if (file == NULL)
709
 
    {
710
 
      olderr = fixup_open_errno (errno);
711
 
      
712
 
      smbc_stat = smbc_getFunctionStat (op_backend->smb_context);
713
 
      res = smbc_stat (op_backend->smb_context, uri, &st);
714
 
      g_free (uri);
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"));
719
 
      else
720
 
        g_vfs_job_failed_from_errno (G_VFS_JOB (job), olderr);
721
 
  }
722
 
  else
723
 
    {
724
 
      
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));
728
 
    }
729
 
}
730
 
 
731
 
static void
732
 
do_read (GVfsBackend *backend,
733
 
         GVfsJobRead *job,
734
 
         GVfsBackendHandle handle,
735
 
         char *buffer,
736
 
         gsize bytes_requested)
737
 
{
738
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
739
 
  ssize_t res;
740
 
  smbc_read_fn smbc_read;
741
 
 
742
 
  /* libsmbclient limits blocksize to (64*1024)-2 for Windows servers,
743
 
   * let's do the same here to achieve reasonable performance. (#588391)
744
 
   *
745
 
   * TODO: port to pull mechanism (#592468)
746
 
   */
747
 
  if (bytes_requested > 65534)
748
 
    bytes_requested = 65534;
749
 
 
750
 
  smbc_read = smbc_getFunctionRead (op_backend->smb_context);
751
 
  res = smbc_read (op_backend->smb_context, (SMBCFILE *)handle, buffer, bytes_requested);
752
 
 
753
 
  if (res == -1)
754
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
755
 
  else
756
 
    {
757
 
      g_vfs_job_read_set_size (job, res);
758
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
759
 
    }
760
 
}
761
 
 
762
 
static void
763
 
do_seek_on_read (GVfsBackend *backend,
764
 
                 GVfsJobSeekRead *job,
765
 
                 GVfsBackendHandle handle,
766
 
                 goffset    offset,
767
 
                 GSeekType  type)
768
 
{
769
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
770
 
  int whence;
771
 
  off_t res;
772
 
  smbc_lseek_fn smbc_lseek;
773
 
 
774
 
  switch (type)
775
 
    {
776
 
    case G_SEEK_SET:
777
 
      whence = SEEK_SET;
778
 
      break;
779
 
    case G_SEEK_CUR:
780
 
      whence = SEEK_CUR;
781
 
      break;
782
 
    case G_SEEK_END:
783
 
      whence = SEEK_END;
784
 
      break;
785
 
    default:
786
 
      g_vfs_job_failed (G_VFS_JOB (job),
787
 
                        G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
788
 
                        _("Unsupported seek type"));
789
 
      return;
790
 
    }
791
 
 
792
 
  smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
793
 
  res = smbc_lseek (op_backend->smb_context, (SMBCFILE *)handle, offset, whence);
794
 
 
795
 
  if (res == (off_t)-1)
796
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
797
 
  else
798
 
    {
799
 
      g_vfs_job_seek_read_set_offset (job, res);
800
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
801
 
    }
802
 
 
803
 
  return;
804
 
}
805
 
 
806
 
static void
807
 
do_query_info_on_read (GVfsBackend *backend,
808
 
                       GVfsJobQueryInfoRead *job,
809
 
                       GVfsBackendHandle handle,
810
 
                       GFileInfo *info,
811
 
                       GFileAttributeMatcher *matcher)
812
 
{
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;
817
 
 
818
 
  smbc_fstat = smbc_getFunctionFstat (op_backend->smb_context);
819
 
  res = smbc_fstat (op_backend->smb_context, (SMBCFILE *)handle, &st);
820
 
  saved_errno = errno;
821
 
 
822
 
  if (res == 0)
823
 
    {
824
 
      set_info_from_stat (op_backend, info, &st, NULL, matcher);
825
 
      
826
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
827
 
    }
828
 
  else
829
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), saved_errno);
830
 
 
831
 
}
832
 
 
833
 
static void
834
 
do_close_read (GVfsBackend *backend,
835
 
               GVfsJobCloseRead *job,
836
 
               GVfsBackendHandle handle)
837
 
{
838
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
839
 
  ssize_t res;
840
 
  smbc_close_fn smbc_close;
841
 
 
842
 
  smbc_close = smbc_getFunctionClose (op_backend->smb_context);
843
 
  res = smbc_close (op_backend->smb_context, (SMBCFILE *)handle);
844
 
  if (res == -1)
845
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
846
 
  else
847
 
    g_vfs_job_succeeded (G_VFS_JOB (job));
848
 
}
849
 
 
850
 
typedef struct {
851
 
  SMBCFILE *file;
852
 
  char *uri;
853
 
  char *tmp_uri;
854
 
  char *backup_uri;
855
 
} SmbWriteHandle;
856
 
 
857
 
static void
858
 
smb_write_handle_free (SmbWriteHandle *handle)
859
 
{
860
 
  g_free (handle->uri);
861
 
  g_free (handle->tmp_uri);
862
 
  g_free (handle->backup_uri);
863
 
  g_free (handle);
864
 
}
865
 
 
866
 
static void
867
 
do_create (GVfsBackend *backend,
868
 
           GVfsJobOpenForWrite *job,
869
 
           const char *filename,
870
 
           GFileCreateFlags flags)
871
 
{
872
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
873
 
  char *uri;
874
 
  SMBCFILE *file;
875
 
  SmbWriteHandle *handle;
876
 
  smbc_open_fn smbc_open;
877
 
  int errsv;
878
 
 
879
 
  uri = create_smb_uri (op_backend->server, op_backend->share, filename);
880
 
  smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
881
 
  errno = 0;
882
 
  file = smbc_open (op_backend->smb_context, uri,
883
 
                    O_CREAT|O_WRONLY|O_EXCL, 0666);
884
 
  g_free (uri);
885
 
 
886
 
  if (file == NULL)
887
 
    {
888
 
      errsv = fixup_open_errno (errno);
889
 
 
890
 
      /* We guarantee EEXIST on create on existing dir */
891
 
      if (errsv == EISDIR)
892
 
        errsv = EEXIST;
893
 
      g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
894
 
    }
895
 
  else
896
 
    {
897
 
      handle = g_new0 (SmbWriteHandle, 1);
898
 
      handle->file = file;
899
 
 
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));
903
 
    }
904
 
}
905
 
 
906
 
static void
907
 
do_append_to (GVfsBackend *backend,
908
 
              GVfsJobOpenForWrite *job,
909
 
              const char *filename,
910
 
              GFileCreateFlags flags)
911
 
{
912
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
913
 
  char *uri;
914
 
  SMBCFILE *file;
915
 
  SmbWriteHandle *handle;
916
 
  off_t initial_offset;
917
 
  smbc_open_fn smbc_open;
918
 
  smbc_lseek_fn smbc_lseek;
919
 
 
920
 
  uri = create_smb_uri (op_backend->server, op_backend->share, filename);
921
 
  smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
922
 
  errno = 0;
923
 
  file = smbc_open (op_backend->smb_context, uri,
924
 
                                        O_CREAT|O_WRONLY|O_APPEND, 0666);
925
 
  g_free (uri);
926
 
 
927
 
  if (file == NULL)
928
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), fixup_open_errno (errno));
929
 
  else
930
 
    {
931
 
      handle = g_new0 (SmbWriteHandle, 1);
932
 
      handle->file = file;
933
 
 
934
 
      smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
935
 
      initial_offset = smbc_lseek (op_backend->smb_context, file,
936
 
                                                       0, SEEK_CUR);
937
 
      if (initial_offset == (off_t) -1)
938
 
        g_vfs_job_open_for_write_set_can_seek (job, FALSE);
939
 
      else
940
 
        {
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);
943
 
        }
944
 
      g_vfs_job_open_for_write_set_handle (job, handle);
945
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
946
 
    }
947
 
}
948
 
 
949
 
 
950
 
static void
951
 
random_chars (char *str, int len)
952
 
{
953
 
  int i;
954
 
  const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
955
 
 
956
 
  for (i = 0; i < len; i++)
957
 
    str[i] = chars[g_random_int_range (0, strlen(chars))];
958
 
}
959
 
 
960
 
static char *
961
 
get_dir_from_uri (const char *uri)
962
 
{
963
 
  const char *prefix_end;
964
 
 
965
 
  prefix_end = uri + strlen (uri);
966
 
 
967
 
  /* Skip slashes at end */
968
 
  while (prefix_end > uri &&
969
 
         *(prefix_end - 1) == '/')
970
 
    prefix_end--;
971
 
 
972
 
  /* Skip to next slash */
973
 
  while (prefix_end > uri &&
974
 
         *(prefix_end - 1) != '/')
975
 
    prefix_end--;
976
 
 
977
 
  return g_strndup (uri, prefix_end - uri);
978
 
}
979
 
 
980
 
static SMBCFILE *
981
 
open_tmpfile (GVfsBackendSmb *backend,
982
 
              const char *uri,
983
 
              char **tmp_uri_out)
984
 
{
985
 
  char *dir_uri, *tmp_uri;
986
 
  char filename[] = "~gvfXXXX.tmp";
987
 
  SMBCFILE *file;
988
 
  smbc_open_fn smbc_open;
989
 
 
990
 
  dir_uri = get_dir_from_uri (uri);
991
 
 
992
 
  do {
993
 
    random_chars (filename + 4, 4);
994
 
    tmp_uri = g_strconcat (dir_uri, filename, NULL);
995
 
 
996
 
    smbc_open = smbc_getFunctionOpen (backend->smb_context);
997
 
    errno = 0;
998
 
    file = smbc_open (backend->smb_context, tmp_uri,
999
 
                      O_CREAT|O_WRONLY|O_EXCL, 0666);
1000
 
  } while (file == NULL && errno == EEXIST);
1001
 
 
1002
 
  g_free (dir_uri);
1003
 
  
1004
 
  if (file)
1005
 
    {
1006
 
      *tmp_uri_out = tmp_uri;
1007
 
      return file;
1008
 
    }
1009
 
  else
1010
 
    {
1011
 
      g_free (tmp_uri);
1012
 
      return NULL;
1013
 
    }
1014
 
}
1015
 
 
1016
 
static gboolean
1017
 
copy_file (GVfsBackendSmb *backend,
1018
 
           GVfsJob *job,
1019
 
           const char *from_uri,
1020
 
           const char *to_uri)
1021
 
{
1022
 
  SMBCFILE *from_file, *to_file;
1023
 
  char buffer[4096];
1024
 
  size_t buffer_size;
1025
 
  ssize_t res;
1026
 
  char *p;
1027
 
  gboolean succeeded;
1028
 
  smbc_open_fn smbc_open;
1029
 
  smbc_read_fn smbc_read;
1030
 
  smbc_write_fn smbc_write;
1031
 
  smbc_close_fn smbc_close;
1032
 
  
1033
 
 
1034
 
  from_file = NULL;
1035
 
  to_file = NULL;
1036
 
 
1037
 
  succeeded = FALSE;
1038
 
 
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);
1043
 
 
1044
 
  from_file = smbc_open (backend->smb_context, from_uri,
1045
 
                         O_RDONLY, 0666);
1046
 
  if (from_file == NULL || g_vfs_job_is_cancelled (job))
1047
 
    goto out;
1048
 
  
1049
 
  to_file = smbc_open (backend->smb_context, to_uri,
1050
 
                       O_CREAT|O_WRONLY|O_TRUNC, 0666);
1051
 
  
1052
 
  if (from_file == NULL || g_vfs_job_is_cancelled (job))
1053
 
    goto out;
1054
 
 
1055
 
  while (1)
1056
 
    {
1057
 
      
1058
 
      res = smbc_read (backend->smb_context, from_file,
1059
 
                                        buffer, sizeof(buffer));
1060
 
      if (res < 0 || g_vfs_job_is_cancelled (job))
1061
 
        goto out;
1062
 
      if (res == 0)
1063
 
        break; /* Succeeded */
1064
 
 
1065
 
      buffer_size = res;
1066
 
      p = buffer;
1067
 
      while (buffer_size > 0)
1068
 
        {
1069
 
          res = smbc_write (backend->smb_context, to_file,
1070
 
                                             p, buffer_size);
1071
 
          if (res < 0 || g_vfs_job_is_cancelled (job))
1072
 
            goto out;
1073
 
          buffer_size -= res;
1074
 
          p += res;
1075
 
        }
1076
 
    }
1077
 
  succeeded = TRUE;
1078
 
 
1079
 
 out: 
1080
 
  if (to_file)
1081
 
          smbc_close (backend->smb_context, to_file);
1082
 
  if (from_file)
1083
 
          smbc_close (backend->smb_context, from_file);
1084
 
  return succeeded;
1085
 
}
1086
 
 
1087
 
static char *
1088
 
create_etag (struct stat *statbuf)
1089
 
{
1090
 
  return g_strdup_printf ("%lu", (long unsigned int)statbuf->st_mtime);
1091
 
}
1092
 
 
1093
 
static void
1094
 
do_replace (GVfsBackend *backend,
1095
 
            GVfsJobOpenForWrite *job,
1096
 
            const char *filename,
1097
 
            const char *etag,
1098
 
            gboolean make_backup,
1099
 
            GFileCreateFlags flags)
1100
 
{
1101
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1102
 
  struct stat original_stat;
1103
 
  int res;
1104
 
  char *uri, *tmp_uri, *backup_uri, *current_etag;
1105
 
  SMBCFILE *file;
1106
 
  GError *error = NULL;
1107
 
  SmbWriteHandle *handle;
1108
 
  smbc_open_fn smbc_open;
1109
 
  smbc_stat_fn smbc_stat;
1110
 
 
1111
 
  uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1112
 
  tmp_uri = NULL;
1113
 
  if (make_backup)
1114
 
    backup_uri = g_strconcat (uri, "~", NULL);
1115
 
  else
1116
 
    backup_uri = NULL;
1117
 
 
1118
 
  smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
1119
 
  smbc_stat = smbc_getFunctionStat (op_backend->smb_context);
1120
 
  
1121
 
  errno = 0;
1122
 
  file = smbc_open (op_backend->smb_context, uri,
1123
 
                    O_CREAT|O_WRONLY|O_EXCL, 0);
1124
 
  if (file == NULL && errno != EEXIST)
1125
 
    {
1126
 
      int errsv = fixup_open_errno (errno);
1127
 
 
1128
 
      g_set_error_literal (&error, G_IO_ERROR,
1129
 
                           g_io_error_from_errno (errsv),
1130
 
                           g_strerror (errsv));
1131
 
      goto error;
1132
 
    }
1133
 
  else if (file == NULL && errno == EEXIST)
1134
 
    {
1135
 
      if (etag != NULL)
1136
 
        {
1137
 
          res = smbc_stat (op_backend->smb_context, uri, &original_stat);
1138
 
          
1139
 
          if (res == 0)
1140
 
            {
1141
 
              current_etag = create_etag (&original_stat);
1142
 
              if (strcmp (etag, current_etag) != 0)
1143
 
                {
1144
 
                  g_free (current_etag);
1145
 
                  g_set_error_literal (&error,
1146
 
                                       G_IO_ERROR,
1147
 
                                       G_IO_ERROR_WRONG_ETAG,
1148
 
                                       _("The file was externally modified"));
1149
 
                  goto error;
1150
 
                }
1151
 
              g_free (current_etag);
1152
 
            }
1153
 
        }
1154
 
      
1155
 
      /* Backup strategy:
1156
 
       *
1157
 
       * By default we:
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
1162
 
       *
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.
1166
 
       */
1167
 
 
1168
 
      file = open_tmpfile (op_backend, uri, &tmp_uri);
1169
 
      if (file == NULL)
1170
 
        {
1171
 
          if (make_backup)
1172
 
            {
1173
 
              if (!copy_file (op_backend, G_VFS_JOB (job), uri, backup_uri))
1174
 
                {
1175
 
                  if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
1176
 
                    g_set_error_literal (&error,
1177
 
                                         G_IO_ERROR,
1178
 
                                         G_IO_ERROR_CANCELLED,
1179
 
                                         _("Operation was cancelled"));
1180
 
                  else
1181
 
                    g_set_error_literal (&error,
1182
 
                                         G_IO_ERROR,
1183
 
                                         G_IO_ERROR_CANT_CREATE_BACKUP,
1184
 
                                         _("Backup file creation failed"));
1185
 
                  goto error;
1186
 
                }
1187
 
              g_free (backup_uri);
1188
 
              backup_uri = NULL;
1189
 
            }
1190
 
          
1191
 
          errno = 0;
1192
 
          file = smbc_open (op_backend->smb_context, uri,
1193
 
                            O_CREAT|O_WRONLY|O_TRUNC, 0);
1194
 
          if (file == NULL)
1195
 
            {
1196
 
              int errsv = fixup_open_errno (errno);
1197
 
 
1198
 
              g_set_error_literal (&error, G_IO_ERROR,
1199
 
                                   g_io_error_from_errno (errsv),
1200
 
                                   g_strerror (errsv));
1201
 
              goto error;
1202
 
            }
1203
 
        }
1204
 
    }
1205
 
  else
1206
 
    {
1207
 
      /* Doesn't exist. Just write away */
1208
 
      g_free (backup_uri);
1209
 
      backup_uri = NULL;
1210
 
    }
1211
 
 
1212
 
  handle = g_new (SmbWriteHandle, 1);
1213
 
  handle->file = file;
1214
 
  handle->uri = uri;
1215
 
  handle->tmp_uri = tmp_uri;
1216
 
  handle->backup_uri = backup_uri;
1217
 
  
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));
1221
 
  
1222
 
  return;
1223
 
  
1224
 
 error:
1225
 
  g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1226
 
  g_error_free (error);
1227
 
  g_free (backup_uri);
1228
 
  g_free (tmp_uri);
1229
 
  g_free (uri);
1230
 
}
1231
 
 
1232
 
 
1233
 
static void
1234
 
do_write (GVfsBackend *backend,
1235
 
          GVfsJobWrite *job,
1236
 
          GVfsBackendHandle _handle,
1237
 
          char *buffer,
1238
 
          gsize buffer_size)
1239
 
{
1240
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1241
 
  SmbWriteHandle *handle = _handle;
1242
 
  ssize_t res;
1243
 
  smbc_write_fn smbc_write;
1244
 
 
1245
 
  smbc_write = smbc_getFunctionWrite (op_backend->smb_context);
1246
 
  res = smbc_write (op_backend->smb_context, handle->file,
1247
 
                                        buffer, buffer_size);
1248
 
  if (res == -1)
1249
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
1250
 
  else
1251
 
    {
1252
 
      g_vfs_job_write_set_written_size (job, res);
1253
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
1254
 
    }
1255
 
}
1256
 
 
1257
 
static void
1258
 
do_seek_on_write (GVfsBackend *backend,
1259
 
                  GVfsJobSeekWrite *job,
1260
 
                  GVfsBackendHandle _handle,
1261
 
                  goffset    offset,
1262
 
                  GSeekType  type)
1263
 
{
1264
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1265
 
  SmbWriteHandle *handle = _handle;
1266
 
  int whence;
1267
 
  off_t res;
1268
 
  smbc_lseek_fn smbc_lseek;
1269
 
 
1270
 
  switch (type)
1271
 
    {
1272
 
    case G_SEEK_SET:
1273
 
      whence = SEEK_SET;
1274
 
      break;
1275
 
    case G_SEEK_CUR:
1276
 
      whence = SEEK_CUR;
1277
 
      break;
1278
 
    case G_SEEK_END:
1279
 
      whence = SEEK_END;
1280
 
      break;
1281
 
    default:
1282
 
      g_vfs_job_failed (G_VFS_JOB (job),
1283
 
                        G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1284
 
                        _("Unsupported seek type"));
1285
 
      return;
1286
 
    }
1287
 
 
1288
 
  smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
1289
 
  res = smbc_lseek (op_backend->smb_context, handle->file, offset, whence);
1290
 
 
1291
 
  if (res == (off_t)-1)
1292
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
1293
 
  else
1294
 
    {
1295
 
      g_vfs_job_seek_write_set_offset (job, res);
1296
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
1297
 
    }
1298
 
 
1299
 
  return;
1300
 
}
1301
 
 
1302
 
static void
1303
 
do_query_info_on_write (GVfsBackend *backend,
1304
 
                        GVfsJobQueryInfoWrite *job,
1305
 
                        GVfsBackendHandle _handle,
1306
 
                        GFileInfo *info,
1307
 
                        GFileAttributeMatcher *matcher)
1308
 
{
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;
1314
 
 
1315
 
  smbc_fstat = smbc_getFunctionFstat (op_backend->smb_context);
1316
 
  res = smbc_fstat (op_backend->smb_context, handle->file, &st);
1317
 
  saved_errno = errno;
1318
 
 
1319
 
  if (res == 0)
1320
 
    {
1321
 
      set_info_from_stat (op_backend, info, &st, NULL, matcher);
1322
 
      
1323
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
1324
 
    }
1325
 
  else
1326
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), saved_errno);
1327
 
 
1328
 
}
1329
 
 
1330
 
static void
1331
 
do_close_write (GVfsBackend *backend,
1332
 
                GVfsJobCloseWrite *job,
1333
 
                GVfsBackendHandle _handle)
1334
 
{
1335
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1336
 
  SmbWriteHandle *handle = _handle;
1337
 
  struct stat stat_at_close;
1338
 
  int stat_res;
1339
 
  ssize_t res;
1340
 
  smbc_fstat_fn smbc_fstat;
1341
 
  smbc_close_fn smbc_close;
1342
 
  smbc_unlink_fn smbc_unlink;
1343
 
  smbc_rename_fn smbc_rename;
1344
 
 
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);
1349
 
  
1350
 
  stat_res = smbc_fstat (op_backend->smb_context, handle->file, &stat_at_close);
1351
 
  
1352
 
  res = smbc_close (op_backend->smb_context, handle->file);
1353
 
 
1354
 
  if (res == -1)
1355
 
    {
1356
 
      g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
1357
 
      
1358
 
      if (handle->tmp_uri)
1359
 
          smbc_unlink (op_backend->smb_context, handle->tmp_uri);
1360
 
      goto out;
1361
 
    }
1362
 
 
1363
 
  if (handle->tmp_uri)
1364
 
    {
1365
 
      if (handle->backup_uri)
1366
 
        {
1367
 
          res = smbc_rename (op_backend->smb_context, handle->uri,
1368
 
                                                 op_backend->smb_context, handle->backup_uri);
1369
 
          if (res ==  -1)
1370
 
            {
1371
 
              int errsv = errno;
1372
 
 
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));
1377
 
              goto out;
1378
 
            }
1379
 
        }
1380
 
      else
1381
 
        smbc_unlink (op_backend->smb_context, handle->uri);
1382
 
      
1383
 
      res = smbc_rename (op_backend->smb_context, handle->tmp_uri,
1384
 
                                             op_backend->smb_context, handle->uri);
1385
 
      if (res ==  -1)
1386
 
        {
1387
 
          smbc_unlink (op_backend->smb_context, handle->tmp_uri);
1388
 
          g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
1389
 
          goto out;
1390
 
        }
1391
 
    }
1392
 
  
1393
 
  if (stat_res == 0)
1394
 
    {
1395
 
      char *etag;
1396
 
      etag = create_etag (&stat_at_close);
1397
 
      g_vfs_job_close_write_set_etag (job, etag);
1398
 
      g_free (etag);
1399
 
    }
1400
 
  
1401
 
  g_vfs_job_succeeded (G_VFS_JOB (job));
1402
 
 
1403
 
 out:
1404
 
  smb_write_handle_free (handle);  
1405
 
}
1406
 
 
1407
 
static void
1408
 
set_info_from_stat (GVfsBackendSmb *backend,
1409
 
                    GFileInfo *info,
1410
 
                    struct stat *statbuf,
1411
 
                    const char *basename,
1412
 
                    GFileAttributeMatcher *matcher)
1413
 
{
1414
 
  GFileType file_type;
1415
 
  GTimeVal t;
1416
 
  GIcon *icon;
1417
 
  char *content_type;
1418
 
  char *display_name;
1419
 
 
1420
 
  if (basename)
1421
 
    {
1422
 
      g_file_info_set_name (info, basename);
1423
 
      if (*basename == '.')
1424
 
        g_file_info_set_is_hidden (info, TRUE);
1425
 
    }
1426
 
 
1427
 
  
1428
 
  if (basename != NULL &&
1429
 
      g_file_attribute_matcher_matches (matcher,
1430
 
                                        G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
1431
 
    {
1432
 
      if (strcmp (basename, "/") == 0)
1433
 
        display_name = g_strdup_printf (_("%s on %s"), backend->share, backend->server);
1434
 
      else
1435
 
        display_name = g_filename_display_name (basename);
1436
 
      
1437
 
      if (strstr (display_name, "\357\277\275") != NULL)
1438
 
        {
1439
 
          char *p = display_name;
1440
 
          display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
1441
 
          g_free (p);
1442
 
        }
1443
 
      g_file_info_set_display_name (info, display_name);
1444
 
      g_free (display_name);
1445
 
    }
1446
 
  
1447
 
  if (basename != NULL &&
1448
 
      g_file_attribute_matcher_matches (matcher,
1449
 
                                        G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME))
1450
 
    {
1451
 
      char *edit_name = g_filename_display_name (basename);
1452
 
      g_file_info_set_edit_name (info, edit_name);
1453
 
      g_free (edit_name);
1454
 
    }
1455
 
 
1456
 
  
1457
 
  file_type = G_FILE_TYPE_UNKNOWN;
1458
 
 
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)
1466
 
#ifdef S_ISSOCK
1467
 
           || S_ISSOCK (statbuf->st_mode)
1468
 
#endif
1469
 
           )
1470
 
    file_type = G_FILE_TYPE_SPECIAL;
1471
 
  else if (S_ISLNK (statbuf->st_mode))
1472
 
    file_type = G_FILE_TYPE_SYMBOLIC_LINK;
1473
 
 
1474
 
  g_file_info_set_file_type (info, file_type);
1475
 
  g_file_info_set_size (info, statbuf->st_size);
1476
 
 
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;
1482
 
#else
1483
 
  t.tv_usec = 0;
1484
 
#endif
1485
 
  g_file_info_set_modification_time (info, &t);
1486
 
 
1487
 
 
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))
1492
 
    {
1493
 
      icon = NULL;
1494
 
      content_type = NULL;
1495
 
      
1496
 
      if (S_ISDIR(statbuf->st_mode))
1497
 
        {
1498
 
          content_type = g_strdup ("inode/directory");
1499
 
          if (basename != NULL && strcmp (basename, "/") == 0)
1500
 
            icon = g_themed_icon_new ("folder-remote");
1501
 
          else
1502
 
            icon = g_themed_icon_new ("folder");
1503
 
        }
1504
 
      else if (basename != NULL)
1505
 
        {
1506
 
          content_type = g_content_type_guess (basename, NULL, 0, NULL);
1507
 
          if (content_type)
1508
 
            icon = g_content_type_get_icon (content_type);
1509
 
        }
1510
 
      
1511
 
      if (content_type)
1512
 
        {
1513
 
          g_file_info_set_content_type (info, content_type);
1514
 
          g_free (content_type);
1515
 
        }
1516
 
 
1517
 
      if (icon == NULL)
1518
 
        icon = g_themed_icon_new ("text-x-generic");
1519
 
      
1520
 
      g_file_info_set_icon (info, icon);
1521
 
      g_object_unref (icon);
1522
 
  }
1523
 
  
1524
 
  /* Don't trust n_link, uid, gid, etc returned from libsmb, its just made up.
1525
 
     These are ok though: */
1526
 
 
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);
1529
 
 
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);
1534
 
 
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);
1540
 
#endif
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);
1546
 
#endif
1547
 
 
1548
 
  /* Libsmb sets the X bit on files to indicate some special things: */
1549
 
  if ((statbuf->st_mode & S_IFDIR) == 0) {
1550
 
    
1551
 
    if (statbuf->st_mode & S_IXOTH)
1552
 
      g_file_info_set_is_hidden (info, TRUE);
1553
 
    
1554
 
    if (statbuf->st_mode & S_IXUSR)
1555
 
      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE, TRUE);
1556
 
    
1557
 
    if (statbuf->st_mode & S_IXGRP)
1558
 
      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_DOS_IS_SYSTEM, TRUE);
1559
 
  }
1560
 
 
1561
 
  if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_ETAG_VALUE))
1562
 
    {
1563
 
      char *etag = create_etag (statbuf);
1564
 
      g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
1565
 
      g_free (etag);
1566
 
    }
1567
 
}
1568
 
 
1569
 
static void
1570
 
do_query_info (GVfsBackend *backend,
1571
 
               GVfsJobQueryInfo *job,
1572
 
               const char *filename,
1573
 
               GFileQueryInfoFlags flags,
1574
 
               GFileInfo *info,
1575
 
               GFileAttributeMatcher *matcher)
1576
 
{
1577
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1578
 
  struct stat st = {0};
1579
 
  char *uri;
1580
 
  int res, saved_errno;
1581
 
  char *basename;
1582
 
  smbc_stat_fn smbc_stat;
1583
 
 
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;
1588
 
  g_free (uri);
1589
 
 
1590
 
  if (res == 0)
1591
 
    {
1592
 
      basename = g_path_get_basename (filename);
1593
 
      set_info_from_stat (op_backend, info, &st, basename, matcher);
1594
 
      g_free (basename);
1595
 
      
1596
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
1597
 
    }
1598
 
  else
1599
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), saved_errno);
1600
 
 
1601
 
}
1602
 
 
1603
 
static void
1604
 
do_query_fs_info (GVfsBackend *backend,
1605
 
                  GVfsJobQueryFsInfo *job,
1606
 
                  const char *filename,
1607
 
                  GFileInfo *info,
1608
 
                  GFileAttributeMatcher *attribute_matcher)
1609
 
{
1610
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1611
 
 
1612
 
  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs");
1613
 
 
1614
 
#ifdef HAVE_SAMBA_STAT_VFS
1615
 
  smbc_statvfs_fn smbc_statvfs;
1616
 
  struct statvfs st = {0};
1617
 
  char *uri;
1618
 
  int res;
1619
 
 
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))
1626
 
    {
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);
1630
 
      g_free (uri);
1631
 
 
1632
 
      if (res == 0)
1633
 
        {
1634
 
          /* older samba versions ( < 3.0.28) return zero values in struct statvfs */
1635
 
          if (st.f_blocks > 0)
1636
 
            {
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
1640
 
               */
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);
1644
 
            }
1645
 
        }
1646
 
    }
1647
 
#endif
1648
 
 
1649
 
  g_vfs_job_succeeded (G_VFS_JOB (job));
1650
 
}
1651
 
 
1652
 
 
1653
 
static gboolean
1654
 
try_query_settable_attributes (GVfsBackend *backend,
1655
 
                               GVfsJobQueryAttributes *job,
1656
 
                               const char *filename)
1657
 
{
1658
 
  GFileAttributeInfoList *list;
1659
 
 
1660
 
  list = g_file_attribute_info_list_new ();
1661
 
 
1662
 
  /* TODO: Add all settable attributes here -- bug #559586 */
1663
 
  /* TODO: xattrs support? */
1664
 
 
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);
1670
 
 
1671
 
#if 0
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);
1680
 
#endif
1681
 
 
1682
 
  g_vfs_job_query_attributes_set_list (job, list);
1683
 
  g_vfs_job_succeeded (G_VFS_JOB (job));
1684
 
  
1685
 
  g_file_attribute_info_list_unref (list);
1686
 
  return TRUE;
1687
 
}
1688
 
 
1689
 
static void
1690
 
do_set_attribute (GVfsBackend *backend,
1691
 
                  GVfsJobSetAttribute *job,
1692
 
                  const char *filename,
1693
 
                  const char *attribute,
1694
 
                  GFileAttributeType type,
1695
 
                  gpointer value_p,
1696
 
                  GFileQueryInfoFlags flags)
1697
 
{
1698
 
  GVfsBackendSmb *op_backend;
1699
 
  char *uri;
1700
 
  int res, errsv;
1701
 
  struct timeval tbuf[2];
1702
 
  smbc_utimes_fn smbc_utimes;
1703
 
#if 0
1704
 
  smbc_chmod_fn smbc_chmod;
1705
 
#endif
1706
 
 
1707
 
 
1708
 
  op_backend = G_VFS_BACKEND_SMB (backend);
1709
 
 
1710
 
  if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) != 0
1711
 
#if 0
1712
 
      && strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) != 0
1713
 
#endif
1714
 
      )
1715
 
    {
1716
 
      g_vfs_job_failed (G_VFS_JOB (job),
1717
 
                        G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1718
 
                        _("Operation unsupported"));
1719
 
      return;
1720
 
    }
1721
 
 
1722
 
  uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1723
 
  res = -1;
1724
 
 
1725
 
  if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
1726
 
    {
1727
 
      if (type != G_FILE_ATTRIBUTE_TYPE_UINT64) 
1728
 
        {
1729
 
          g_vfs_job_failed (G_VFS_JOB (job),
1730
 
                            G_IO_ERROR,
1731
 
                            G_IO_ERROR_INVALID_ARGUMENT,
1732
 
                            "%s",
1733
 
                            _("Invalid attribute type (uint64 expected)"));
1734
 
        }
1735
 
 
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]);
1743
 
    }
1744
 
#if 0
1745
 
  else
1746
 
  if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
1747
 
    {
1748
 
      smbc_chmod = smbc_getFunctionChmod (op_backend->smb_context);
1749
 
      res = smbc_chmod (op_backend->smb_context, uri, (*(guint32 *)value_p) & 0777);
1750
 
    }
1751
 
#endif    
1752
 
 
1753
 
  errsv = errno;
1754
 
  g_free (uri);
1755
 
 
1756
 
  if (res != 0)
1757
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
1758
 
  else
1759
 
    g_vfs_job_succeeded (G_VFS_JOB (job));
1760
 
}
1761
 
 
1762
 
static void
1763
 
do_enumerate (GVfsBackend *backend,
1764
 
              GVfsJobEnumerate *job,
1765
 
              const char *filename,
1766
 
              GFileAttributeMatcher *matcher,
1767
 
              GFileQueryInfoFlags flags)
1768
 
{
1769
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1770
 
  struct stat st;
1771
 
  int res;
1772
 
  GError *error;
1773
 
  SMBCFILE *dir;
1774
 
  char dirents[1024*4];
1775
 
  struct smbc_dirent *dirp;
1776
 
  GList *files;
1777
 
  GFileInfo *info;
1778
 
  GString *uri;
1779
 
  int uri_start_len;
1780
 
  smbc_opendir_fn smbc_opendir;
1781
 
  smbc_getdents_fn smbc_getdents;
1782
 
  smbc_stat_fn smbc_stat;
1783
 
  smbc_closedir_fn smbc_closedir;
1784
 
 
1785
 
  uri = create_smb_uri_string (op_backend->server, op_backend->share, filename);
1786
 
  
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);
1791
 
  
1792
 
  dir = smbc_opendir (op_backend->smb_context, uri->str);
1793
 
 
1794
 
  if (dir == NULL)
1795
 
    {
1796
 
      int errsv = errno;
1797
 
 
1798
 
      error = NULL;
1799
 
      g_set_error_literal (&error, G_IO_ERROR,
1800
 
                           g_io_error_from_errno (errsv),
1801
 
                           g_strerror (errsv));
1802
 
      goto error;
1803
 
    }
1804
 
 
1805
 
  g_vfs_job_succeeded (G_VFS_JOB (job));
1806
 
 
1807
 
  if (uri->str[uri->len - 1] != '/')
1808
 
    g_string_append_c (uri, '/');
1809
 
  uri_start_len = uri->len;
1810
 
 
1811
 
  while (TRUE)
1812
 
    {
1813
 
      files = NULL;
1814
 
      
1815
 
      res = smbc_getdents (op_backend->smb_context, dir, (struct smbc_dirent *)dirents, sizeof (dirents));
1816
 
      if (res <= 0)
1817
 
        break;
1818
 
      
1819
 
      dirp = (struct smbc_dirent *)dirents;
1820
 
      while (res > 0)
1821
 
        {
1822
 
          unsigned int dirlen;
1823
 
 
1824
 
          /* TODO: Only do stat if required for flags */
1825
 
          
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)
1831
 
            {
1832
 
              int stat_res;
1833
 
              g_string_truncate (uri, uri_start_len);
1834
 
              g_string_append_encoded (uri,
1835
 
                                       dirp->name,
1836
 
                                       SUB_DELIM_CHARS ":@/");
1837
 
 
1838
 
              if (matcher == NULL ||
1839
 
                  g_file_attribute_matcher_matches_only (matcher, G_FILE_ATTRIBUTE_STANDARD_NAME))
1840
 
                {
1841
 
                  info = g_file_info_new ();
1842
 
                  g_file_info_set_name (info, dirp->name);
1843
 
                  files = g_list_prepend (files, info);
1844
 
                }
1845
 
              else
1846
 
                {
1847
 
                  stat_res = smbc_stat (op_backend->smb_context,
1848
 
                                                            uri->str, &st);
1849
 
                  if (stat_res == 0)
1850
 
                    {
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);
1854
 
                    }
1855
 
                }
1856
 
            }
1857
 
          
1858
 
          dirlen = dirp->dirlen;
1859
 
          dirp = (struct smbc_dirent *) (((char *)dirp) + dirlen);
1860
 
          res -= dirlen;
1861
 
        }
1862
 
      
1863
 
      if (files)
1864
 
        {
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);
1869
 
        }
1870
 
    }
1871
 
      
1872
 
  res = smbc_closedir (op_backend->smb_context, dir);
1873
 
 
1874
 
  g_vfs_job_enumerate_done (job);
1875
 
 
1876
 
  g_string_free (uri, TRUE);
1877
 
  return;
1878
 
  
1879
 
 error:
1880
 
  g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1881
 
  g_error_free (error);
1882
 
  g_string_free (uri, TRUE);
1883
 
}
1884
 
 
1885
 
static void
1886
 
do_set_display_name (GVfsBackend *backend,
1887
 
                     GVfsJobSetDisplayName *job,
1888
 
                     const char *filename,
1889
 
                     const char *display_name)
1890
 
{
1891
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1892
 
  char *from_uri, *to_uri;
1893
 
  char *dirname, *new_path;
1894
 
  int res, errsv;
1895
 
  smbc_rename_fn smbc_rename;
1896
 
 
1897
 
  dirname = g_path_get_dirname (filename);
1898
 
 
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? */
1902
 
 
1903
 
  new_path = g_build_filename (dirname, display_name, NULL);
1904
 
  g_free (dirname);
1905
 
  
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);
1908
 
  
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);
1912
 
  errsv = errno;
1913
 
  g_free (from_uri);
1914
 
  g_free (to_uri);
1915
 
 
1916
 
  if (res != 0)
1917
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
1918
 
  else
1919
 
    {
1920
 
      g_vfs_job_set_display_name_set_new_path (job, new_path);
1921
 
      g_vfs_job_succeeded (G_VFS_JOB (job));
1922
 
    }
1923
 
  g_free (new_path);
1924
 
}
1925
 
 
1926
 
static void
1927
 
do_delete (GVfsBackend *backend,
1928
 
           GVfsJobDelete *job,
1929
 
           const char *filename)
1930
 
{
1931
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1932
 
  struct stat statbuf;
1933
 
  char *uri;
1934
 
  int errsv, res;
1935
 
  smbc_stat_fn smbc_stat;
1936
 
  smbc_rmdir_fn smbc_rmdir;
1937
 
  smbc_unlink_fn smbc_unlink;
1938
 
 
1939
 
 
1940
 
  uri = create_smb_uri (op_backend->server, op_backend->share, filename);
1941
 
 
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);
1945
 
 
1946
 
  res = smbc_stat (op_backend->smb_context, uri, &statbuf);
1947
 
  if (res == -1)
1948
 
    {
1949
 
      errsv = errno;
1950
 
 
1951
 
      g_vfs_job_failed (G_VFS_JOB (job),
1952
 
                        G_IO_ERROR,
1953
 
                        g_io_error_from_errno (errsv),
1954
 
                        _("Error deleting file: %s"),
1955
 
                        g_strerror (errsv));
1956
 
      g_free (uri);
1957
 
      return;
1958
 
    }
1959
 
 
1960
 
  if (S_ISDIR (statbuf.st_mode))
1961
 
    res = smbc_rmdir (op_backend->smb_context, uri);
1962
 
  else
1963
 
    res = smbc_unlink (op_backend->smb_context, uri);
1964
 
  errsv = errno;
1965
 
  g_free (uri);
1966
 
 
1967
 
  if (res != 0)
1968
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
1969
 
  else
1970
 
    g_vfs_job_succeeded (G_VFS_JOB (job));
1971
 
}
1972
 
 
1973
 
static void
1974
 
do_make_directory (GVfsBackend *backend,
1975
 
                   GVfsJobMakeDirectory *job,
1976
 
                   const char *filename)
1977
 
{
1978
 
  GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
1979
 
  char *uri;
1980
 
  int errsv, res;
1981
 
  smbc_mkdir_fn smbc_mkdir;
1982
 
 
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);
1986
 
  errsv = errno;
1987
 
  g_free (uri);
1988
 
 
1989
 
  if (res != 0)
1990
 
    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
1991
 
  else
1992
 
    g_vfs_job_succeeded (G_VFS_JOB (job));
1993
 
}
1994
 
 
1995
 
static void
1996
 
do_move (GVfsBackend *backend,
1997
 
         GVfsJobMove *job,
1998
 
         const char *source,
1999
 
         const char *destination,
2000
 
         GFileCopyFlags flags,
2001
 
         GFileProgressCallback progress_callback,
2002
 
         gpointer progress_callback_data)
2003
 
{
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;
2008
 
  int res, errsv;
2009
 
  smbc_stat_fn smbc_stat;
2010
 
  smbc_rename_fn smbc_rename;
2011
 
  smbc_unlink_fn smbc_unlink;
2012
 
 
2013
 
  
2014
 
  source_uri = create_smb_uri (op_backend->server, op_backend->share, source);
2015
 
 
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);
2019
 
 
2020
 
  res = smbc_stat (op_backend->smb_context, source_uri, &statbuf);
2021
 
  if (res == -1)
2022
 
    {
2023
 
      errsv = errno;
2024
 
 
2025
 
      g_vfs_job_failed (G_VFS_JOB (job),
2026
 
                        G_IO_ERROR,
2027
 
                        g_io_error_from_errno (errsv),
2028
 
                        _("Error moving file: %s"),
2029
 
                        g_strerror (errsv));
2030
 
      g_free (source_uri);
2031
 
      return;
2032
 
    }
2033
 
  else
2034
 
    source_is_dir = S_ISDIR (statbuf.st_mode);
2035
 
 
2036
 
  dest_uri = create_smb_uri (op_backend->server, op_backend->share, destination);
2037
 
  
2038
 
  destination_exist = FALSE;
2039
 
  res = smbc_stat (op_backend->smb_context, dest_uri, &statbuf);
2040
 
  if (res == 0)
2041
 
    {
2042
 
      destination_exist = TRUE; /* Target file exists */
2043
 
 
2044
 
      if (flags & G_FILE_COPY_OVERWRITE)
2045
 
        {
2046
 
          /* Always fail on dirs, even with overwrite */
2047
 
          if (S_ISDIR (statbuf.st_mode))
2048
 
            {
2049
 
              g_vfs_job_failed (G_VFS_JOB (job),
2050
 
                                G_IO_ERROR,
2051
 
                                G_IO_ERROR_WOULD_MERGE,
2052
 
                                _("Can't move directory over directory"));
2053
 
              g_free (source_uri);
2054
 
              g_free (dest_uri);
2055
 
              return;
2056
 
            }
2057
 
        }
2058
 
      else
2059
 
        {
2060
 
          g_vfs_job_failed (G_VFS_JOB (job),
2061
 
                            G_IO_ERROR,
2062
 
                            G_IO_ERROR_EXISTS,
2063
 
                            _("Target file already exists"));
2064
 
          g_free (source_uri);
2065
 
          g_free (dest_uri);
2066
 
          return;
2067
 
        }
2068
 
    }
2069
 
 
2070
 
  if (flags & G_FILE_COPY_BACKUP && destination_exist)
2071
 
    {
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);
2075
 
      if (res == -1)
2076
 
        {
2077
 
          g_vfs_job_failed (G_VFS_JOB (job),
2078
 
                            G_IO_ERROR,
2079
 
                            G_IO_ERROR_CANT_CREATE_BACKUP,
2080
 
                            _("Backup file creation failed"));
2081
 
          g_free (source_uri);
2082
 
          g_free (dest_uri);
2083
 
          g_free (backup_uri);
2084
 
          return;
2085
 
        }
2086
 
      g_free (backup_uri);
2087
 
      destination_exist = FALSE; /* It did, but no more */
2088
 
    }
2089
 
 
2090
 
  if (source_is_dir && destination_exist && (flags & G_FILE_COPY_OVERWRITE))
2091
 
    {
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);
2095
 
      errsv = errno;
2096
 
      if (res == -1)
2097
 
        {
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);
2103
 
          g_free (dest_uri);
2104
 
          return;
2105
 
        }
2106
 
    }
2107
 
 
2108
 
  
2109
 
  res = smbc_rename (op_backend->smb_context, source_uri,
2110
 
                                         op_backend->smb_context, dest_uri);
2111
 
  errsv = errno;
2112
 
  g_free (source_uri);
2113
 
  g_free (dest_uri);
2114
 
 
2115
 
  /* Catch moves across device boundaries */
2116
 
  if (res != 0)
2117
 
    {
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"));
2125
 
      else
2126
 
        g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
2127
 
    }
2128
 
  else
2129
 
    g_vfs_job_succeeded (G_VFS_JOB (job));
2130
 
}
2131
 
 
2132
 
static void
2133
 
g_vfs_backend_smb_class_init (GVfsBackendSmbClass *klass)
2134
 
{
2135
 
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2136
 
  GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
2137
 
  
2138
 
  gobject_class->finalize = g_vfs_backend_smb_finalize;
2139
 
 
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;
2163
 
}
2164
 
 
2165
 
void
2166
 
g_vfs_smb_daemon_init (void)
2167
 
{
2168
 
  g_set_application_name (_("Windows Shares Filesystem Service"));
2169
 
}