Coverage Report

Created: 2025-11-16 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/dosmode.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   dos mode handling functions
4
   Copyright (C) Andrew Tridgell 1992-1998
5
   Copyright (C) James Peach 2006
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
*/
20
21
#include "includes.h"
22
#include "globals.h"
23
#include "system/filesys.h"
24
#include "librpc/gen_ndr/ndr_xattr.h"
25
#include "librpc/gen_ndr/ioctl.h"
26
#include "../libcli/security/security.h"
27
#include "smbd/smbd.h"
28
#include "lib/param/loadparm.h"
29
#include "lib/util/tevent_ntstatus.h"
30
#include "lib/util/string_wrappers.h"
31
#include "fake_file.h"
32
33
static void dos_mode_debug_print(const char *func, uint32_t mode)
34
0
{
35
0
  fstring modestr;
36
37
0
  if (DEBUGLEVEL < DBGLVL_INFO) {
38
0
    return;
39
0
  }
40
41
0
  modestr[0] = '\0';
42
43
0
  if (mode & FILE_ATTRIBUTE_HIDDEN) {
44
0
    fstrcat(modestr, "h");
45
0
  }
46
0
  if (mode & FILE_ATTRIBUTE_READONLY) {
47
0
    fstrcat(modestr, "r");
48
0
  }
49
0
  if (mode & FILE_ATTRIBUTE_SYSTEM) {
50
0
    fstrcat(modestr, "s");
51
0
  }
52
0
  if (mode & FILE_ATTRIBUTE_DIRECTORY) {
53
0
    fstrcat(modestr, "d");
54
0
  }
55
0
  if (mode & FILE_ATTRIBUTE_ARCHIVE) {
56
0
    fstrcat(modestr, "a");
57
0
  }
58
0
  if (mode & FILE_ATTRIBUTE_SPARSE) {
59
0
    fstrcat(modestr, "[sparse]");
60
0
  }
61
0
  if (mode & FILE_ATTRIBUTE_OFFLINE) {
62
0
    fstrcat(modestr, "[offline]");
63
0
  }
64
0
  if (mode & FILE_ATTRIBUTE_COMPRESSED) {
65
0
    fstrcat(modestr, "[compressed]");
66
0
  }
67
0
  if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
68
0
    fstrcat(modestr, "[reparse_point]");
69
0
  }
70
71
0
  DBG_INFO("%s returning (0x%" PRIx32 "): \"%s\"\n",
72
0
     func,
73
0
     mode,
74
0
     modestr);
75
0
}
76
77
static uint32_t filter_mode_by_protocol(enum protocol_types protocol,
78
          uint32_t mode)
79
0
{
80
0
  if (protocol <= PROTOCOL_LANMAN2) {
81
0
    DEBUG(10,("filter_mode_by_protocol: "
82
0
      "filtering result 0x%x to 0x%x\n",
83
0
      (unsigned int)mode,
84
0
      (unsigned int)(mode & 0x3f) ));
85
0
    mode &= 0x3f;
86
0
  }
87
0
  return mode;
88
0
}
89
90
mode_t apply_conf_file_mask(struct connection_struct *conn, mode_t mode)
91
0
{
92
0
  mode &= lp_create_mask(SNUM(conn));
93
0
  mode |= lp_force_create_mode(SNUM(conn));
94
0
  return mode;
95
0
}
96
97
mode_t apply_conf_dir_mask(struct connection_struct *conn, mode_t mode)
98
0
{
99
0
  mode &= lp_directory_mask(SNUM(conn));
100
0
  mode |= lp_force_directory_mode(SNUM(conn));
101
0
  return mode;
102
0
}
103
104
/****************************************************************************
105
 Change a dos mode to a unix mode.
106
    Base permission for files:
107
         if creating file and inheriting (i.e. parent_dir != NULL)
108
           apply read/write bits from parent directory.
109
         else
110
           everybody gets read bit set
111
         dos readonly is represented in unix by removing everyone's write bit
112
         dos archive is represented in unix by the user's execute bit
113
         dos system is represented in unix by the group's execute bit
114
         dos hidden is represented in unix by the other's execute bit
115
         if !inheriting {
116
           Then apply create mask,
117
           then add force bits.
118
         }
119
    Base permission for directories:
120
         dos directory is represented in unix by unix's dir bit and the exec bit
121
         if !inheriting {
122
           Then apply create mask,
123
           then add force bits.
124
         }
125
****************************************************************************/
126
127
mode_t unix_mode(connection_struct *conn, int dosmode,
128
     const struct smb_filename *smb_fname,
129
     struct files_struct *parent_dirfsp)
130
0
{
131
0
  mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
132
0
  mode_t dir_mode = 0; /* Mode of the inherit_from directory if
133
            * inheriting. */
134
135
0
  if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
136
0
      !lp_store_dos_attributes(SNUM(conn))) {
137
0
    result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
138
0
  }
139
140
0
  if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
141
0
    struct stat_ex sbuf = { .st_ex_nlink = 0, };
142
0
    int ret;
143
144
0
    DBG_DEBUG("[%s] inheriting from [%s]\n",
145
0
        smb_fname_str_dbg(smb_fname),
146
0
        smb_fname_str_dbg(parent_dirfsp->fsp_name));
147
148
0
    ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
149
0
    if (ret != 0) {
150
0
      DBG_ERR("fstat failed [%s]: %s\n",
151
0
        smb_fname_str_dbg(parent_dirfsp->fsp_name),
152
0
        strerror(errno));
153
0
      return(0);      /* *** shouldn't happen! *** */
154
0
    }
155
156
    /* Save for later - but explicitly remove setuid bit for safety. */
157
0
    dir_mode = sbuf.st_ex_mode & ~S_ISUID;
158
0
    DEBUG(2,("unix_mode(%s) inherit mode %o\n",
159
0
       smb_fname_str_dbg(smb_fname), (int)dir_mode));
160
    /* Clear "result" */
161
0
    result = 0;
162
0
  }
163
164
0
  if (dosmode & FILE_ATTRIBUTE_DIRECTORY) {
165
    /* We never make directories read only for the owner as under DOS a user
166
    can always create a file in a read-only directory. */
167
0
    result |= (S_IFDIR | S_IWUSR);
168
169
0
    if (dir_mode) {
170
      /* Inherit mode of parent directory. */
171
0
      result |= dir_mode;
172
0
    } else {
173
      /* Provisionally add all 'x' bits */
174
0
      result |= (S_IXUSR | S_IXGRP | S_IXOTH);
175
0
      result = apply_conf_dir_mask(conn, result);
176
0
    }
177
0
  } else {
178
0
    if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
179
0
        lp_map_archive(SNUM(conn))) {
180
0
      result |= S_IXUSR;
181
0
    }
182
183
0
    if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
184
0
        lp_map_system(SNUM(conn))) {
185
0
      result |= S_IXGRP;
186
0
    }
187
188
0
    if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
189
0
        lp_map_hidden(SNUM(conn))) {
190
0
      result |= S_IXOTH;
191
0
    }
192
193
0
    if (dir_mode) {
194
      /* Inherit 666 component of parent directory mode */
195
0
      result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
196
0
    } else {
197
0
      result = apply_conf_file_mask(conn, result);
198
0
    }
199
0
  }
200
201
0
  DBG_INFO("unix_mode(%s) returning 0%o\n",
202
0
     smb_fname_str_dbg(smb_fname), (int)result);
203
204
0
  return(result);
205
0
}
206
207
/****************************************************************************
208
 Change a unix mode to a dos mode.
209
****************************************************************************/
210
211
static uint32_t dos_mode_from_sbuf(connection_struct *conn,
212
           const struct stat_ex *st,
213
           struct files_struct *fsp)
214
0
{
215
0
  int result = 0;
216
0
  enum mapreadonly_options ro_opts =
217
0
    (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
218
219
#if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
220
  /* if we can find out if a file is immutable we should report it r/o */
221
  if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
222
    result |= FILE_ATTRIBUTE_READONLY;
223
  }
224
#endif
225
0
  if (ro_opts == MAP_READONLY_YES) {
226
    /* Original Samba method - map inverse of user "w" bit. */
227
0
    if ((st->st_ex_mode & S_IWUSR) == 0) {
228
0
      result |= FILE_ATTRIBUTE_READONLY;
229
0
    }
230
0
  }
231
0
  if (ro_opts == MAP_READONLY_PERMISSIONS) {
232
    /* smb_fname->fsp can be NULL for an MS-DFS link. */
233
    /* Check actual permissions for read-only. */
234
0
    if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
235
0
      result |= FILE_ATTRIBUTE_READONLY;
236
0
    }
237
0
  }
238
239
0
  if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
240
0
    result |= FILE_ATTRIBUTE_ARCHIVE;
241
0
  }
242
243
0
  if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
244
0
    result |= FILE_ATTRIBUTE_SYSTEM;
245
0
  }
246
247
0
  if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
248
0
    result |= FILE_ATTRIBUTE_HIDDEN;
249
0
  }
250
251
0
  if (S_ISDIR(st->st_ex_mode)) {
252
0
    result = FILE_ATTRIBUTE_DIRECTORY |
253
0
       (result & FILE_ATTRIBUTE_READONLY);
254
0
  }
255
256
0
  dos_mode_debug_print(__func__, result);
257
258
0
  return result;
259
0
}
260
261
/****************************************************************************
262
 Get DOS attributes from an EA.
263
 This can also pull the create time into the stat struct inside smb_fname.
264
****************************************************************************/
265
266
NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
267
          DATA_BLOB blob,
268
          uint32_t *pattr)
269
0
{
270
0
  struct xattr_DOSATTRIB dosattrib;
271
0
  enum ndr_err_code ndr_err;
272
0
  uint32_t dosattr;
273
274
0
  ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
275
0
      (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
276
277
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
278
0
    DBG_WARNING("bad ndr decode "
279
0
          "from EA on file %s: Error = %s\n",
280
0
          smb_fname_str_dbg(smb_fname),
281
0
          ndr_errstr(ndr_err));
282
0
    return ndr_map_error2ntstatus(ndr_err);
283
0
  }
284
285
0
  DBG_DEBUG("%s attr = %s\n",
286
0
      smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
287
288
0
  switch (dosattrib.version) {
289
0
  case 0xFFFF:
290
0
    dosattr = dosattrib.info.compatinfoFFFF.attrib;
291
0
    break;
292
0
  case 1:
293
0
    dosattr = dosattrib.info.info1.attrib;
294
0
    if (!null_nttime(dosattrib.info.info1.create_time)) {
295
0
      struct timespec create_time =
296
0
        nt_time_to_unix_timespec(
297
0
          dosattrib.info.info1.create_time);
298
299
0
      update_stat_ex_create_time(&smb_fname->st,
300
0
               create_time);
301
302
0
      DBG_DEBUG("file %s case 1 set btime %s",
303
0
          smb_fname_str_dbg(smb_fname),
304
0
          time_to_asc(convert_timespec_to_time_t(
305
0
                  create_time)));
306
0
    }
307
0
    break;
308
0
  case 2:
309
0
    dosattr = dosattrib.info.oldinfo2.attrib;
310
    /* Don't know what flags to check for this case. */
311
0
    break;
312
0
  case 3:
313
0
    dosattr = dosattrib.info.info3.attrib;
314
0
    if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
315
0
        !null_nttime(dosattrib.info.info3.create_time)) {
316
0
      struct timespec create_time =
317
0
        nt_time_to_full_timespec(
318
0
          dosattrib.info.info3.create_time);
319
320
0
      update_stat_ex_create_time(&smb_fname->st,
321
0
               create_time);
322
323
0
      DBG_DEBUG("file %s case 3 set btime %s",
324
0
          smb_fname_str_dbg(smb_fname),
325
0
          time_to_asc(convert_timespec_to_time_t(
326
0
                  create_time)));
327
0
    }
328
0
    break;
329
0
  case 4:
330
0
  case 5:
331
0
  {
332
0
    uint32_t info_valid_flags;
333
0
    NTTIME info_create_time;
334
335
0
    if (dosattrib.version == 4) {
336
0
      info_valid_flags = dosattrib.info.info4.valid_flags;
337
0
      info_create_time = dosattrib.info.info4.create_time;
338
0
      dosattr = dosattrib.info.info4.attrib;
339
0
    } else {
340
0
      info_valid_flags = dosattrib.info.info5.valid_flags;
341
0
      info_create_time = dosattrib.info.info5.create_time;
342
0
      dosattr = dosattrib.info.info5.attrib;
343
0
    }
344
345
0
    if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
346
0
        !null_nttime(info_create_time))
347
0
    {
348
0
      struct timespec creat_time;
349
350
0
      creat_time = nt_time_to_full_timespec(info_create_time);
351
0
      update_stat_ex_create_time(&smb_fname->st, creat_time);
352
353
0
      DBG_DEBUG("file [%s] creation time [%s]\n",
354
0
        smb_fname_str_dbg(smb_fname),
355
0
        nt_time_string(talloc_tos(), info_create_time));
356
0
    }
357
358
0
    break;
359
0
  }
360
0
  default:
361
0
    DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
362
0
          smb_fname_str_dbg(smb_fname), blob.data);
363
    /* Should this be INTERNAL_ERROR? */
364
0
    return NT_STATUS_INVALID_PARAMETER;
365
0
  }
366
367
0
  if (S_ISDIR(smb_fname->st.st_ex_mode)) {
368
0
    dosattr |= FILE_ATTRIBUTE_DIRECTORY;
369
0
  }
370
371
  /*
372
   * _SPARSE and _REPARSE_POINT are valid on get but not on
373
   * set. Both are created via special fcntls.
374
   */
375
376
0
  dosattr &= (SAMBA_ATTRIBUTES_MASK|
377
0
        FILE_ATTRIBUTE_SPARSE|
378
0
        FILE_ATTRIBUTE_REPARSE_POINT);
379
380
0
  *pattr |= dosattr;
381
382
0
  dos_mode_debug_print(__func__, *pattr);
383
384
0
  return NT_STATUS_OK;
385
0
}
386
387
NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
388
            uint32_t *pattr)
389
0
{
390
0
  DATA_BLOB blob;
391
0
  ssize_t sizeret;
392
0
  fstring attrstr;
393
0
  NTSTATUS status;
394
395
0
  if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
396
0
    return NT_STATUS_NOT_IMPLEMENTED;
397
0
  }
398
399
  /* Don't reset pattr to zero as we may already have filename-based attributes we
400
     need to preserve. */
401
402
0
  sizeret = SMB_VFS_FGETXATTR(fsp,
403
0
            SAMBA_XATTR_DOS_ATTRIB,
404
0
            attrstr,
405
0
            sizeof(attrstr));
406
0
  if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
407
    /* we may also retrieve dos attribs for unreadable files, this
408
       is why we'll retry as root. We don't use root in the first
409
       run because in cases like NFS, root might have even less
410
       rights than the real user
411
    */
412
0
    become_root();
413
0
    sizeret = SMB_VFS_FGETXATTR(fsp,
414
0
              SAMBA_XATTR_DOS_ATTRIB,
415
0
              attrstr,
416
0
              sizeof(attrstr));
417
0
    unbecome_root();
418
0
  }
419
0
  if (sizeret == -1) {
420
0
    DBG_INFO("Cannot get attribute "
421
0
       "from EA on file %s: Error = %s\n",
422
0
       fsp_str_dbg(fsp), strerror(errno));
423
0
    return map_nt_error_from_unix(errno);
424
0
  }
425
426
0
  blob.data = (uint8_t *)attrstr;
427
0
  blob.length = sizeret;
428
429
0
  status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
430
0
  if (!NT_STATUS_IS_OK(status)) {
431
0
    return status;
432
0
  }
433
434
0
  return NT_STATUS_OK;
435
0
}
436
437
/****************************************************************************
438
 Set DOS attributes in an EA.
439
 Also sets the create time.
440
****************************************************************************/
441
442
NTSTATUS set_ea_dos_attribute(connection_struct *conn,
443
            struct smb_filename *smb_fname,
444
            uint32_t dosmode)
445
0
{
446
0
  struct xattr_DOSATTRIB dosattrib = { .version = 0, };
447
0
  enum ndr_err_code ndr_err;
448
0
  DATA_BLOB blob = { .data = NULL, };
449
0
  struct timespec btime;
450
0
  int ret;
451
452
0
  if (!lp_store_dos_attributes(SNUM(conn))) {
453
0
    return NT_STATUS_NOT_IMPLEMENTED;
454
0
  }
455
456
0
  if (smb_fname->fsp == NULL) {
457
    /* symlink */
458
0
    return NT_STATUS_OBJECT_NAME_NOT_FOUND;
459
0
  }
460
  /*
461
   * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
462
   * vfs_default via DMAPI if that is enabled.
463
   */
464
0
  dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
465
466
0
  dosattrib.version = 5;
467
0
  dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
468
0
          XATTR_DOSINFO_CREATE_TIME;
469
0
  dosattrib.info.info5.attrib = dosmode;
470
0
  dosattrib.info.info5.create_time = full_timespec_to_nt_time(
471
0
    &smb_fname->st.st_ex_btime);
472
473
0
  DBG_DEBUG("set attribute 0x%" PRIx32 ", btime = %s on file %s\n",
474
0
      dosmode,
475
0
      time_to_asc(convert_timespec_to_time_t(
476
0
        smb_fname->st.st_ex_btime)),
477
0
      smb_fname_str_dbg(smb_fname));
478
479
0
  ndr_err = ndr_push_struct_blob(
480
0
      &blob, talloc_tos(), &dosattrib,
481
0
      (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
482
483
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
484
0
    DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
485
0
      ndr_errstr(ndr_err)));
486
0
    return ndr_map_error2ntstatus(ndr_err);
487
0
  }
488
489
0
  if (blob.data == NULL || blob.length == 0) {
490
    /* Should this be INTERNAL_ERROR? */
491
0
    return NT_STATUS_INVALID_PARAMETER;
492
0
  }
493
494
0
  ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
495
0
             SAMBA_XATTR_DOS_ATTRIB,
496
0
             blob.data, blob.length, 0);
497
0
  if (ret != 0) {
498
0
    NTSTATUS status = NT_STATUS_OK;
499
0
    bool set_dosmode_ok = false;
500
501
0
    if ((errno != EPERM) && (errno != EACCES)) {
502
0
      DBG_INFO("Cannot set "
503
0
         "attribute EA on file %s: Error = %s\n",
504
0
         smb_fname_str_dbg(smb_fname), strerror(errno));
505
0
      return map_nt_error_from_unix(errno);
506
0
    }
507
508
    /* We want DOS semantics, ie allow non owner with write permission to change the
509
      bits on a file. Just like file_ntimes below.
510
    */
511
512
    /* Check if we have write access. */
513
0
    if (!CAN_WRITE(conn)) {
514
0
      return NT_STATUS_ACCESS_DENIED;
515
0
    }
516
517
0
    status = smbd_check_access_rights_fsp(conn->cwd_fsp,
518
0
          smb_fname->fsp,
519
0
          false,
520
0
          FILE_WRITE_ATTRIBUTES);
521
0
    if (NT_STATUS_IS_OK(status)) {
522
0
      set_dosmode_ok = true;
523
0
    }
524
525
0
    if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
526
0
      set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
527
0
    }
528
529
0
    if (!set_dosmode_ok) {
530
0
      return NT_STATUS_ACCESS_DENIED;
531
0
    }
532
533
0
    become_root();
534
0
    ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
535
0
          SAMBA_XATTR_DOS_ATTRIB,
536
0
          blob.data, blob.length, 0);
537
0
    if (ret == 0) {
538
0
      status = NT_STATUS_OK;
539
0
    }
540
0
    unbecome_root();
541
0
    if (!NT_STATUS_IS_OK(status)) {
542
0
      return status;
543
0
    }
544
0
  }
545
546
  /*
547
   * We correctly stored the create time.
548
   * We *always* set XATTR_DOSINFO_CREATE_TIME,
549
   * so now it can no longer be considered
550
   * calculated. Make sure to use the value rounded
551
   * to NTTIME granularity we've stored in the xattr.
552
   */
553
0
  btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
554
0
  update_stat_ex_create_time(&smb_fname->st, btime);
555
556
0
  DBG_DEBUG("set EA 0x%" PRIx32 " on file %s\n",
557
0
      dosmode,
558
0
      smb_fname_str_dbg(smb_fname));
559
0
  return NT_STATUS_OK;
560
0
}
561
562
static uint32_t
563
dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
564
0
{
565
0
  const char *p = NULL;
566
0
  uint32_t result = dosmode;
567
568
0
  if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
569
0
      lp_hide_dot_files(SNUM(conn)))
570
0
  {
571
0
    p = strrchr_m(name, '/');
572
0
    if (p) {
573
0
      p++;
574
0
    } else {
575
0
      p = name;
576
0
    }
577
578
    /* Only . and .. are not hidden. */
579
0
    if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
580
0
      result |= FILE_ATTRIBUTE_HIDDEN;
581
0
    }
582
0
  }
583
584
0
  if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
585
0
    result |= FILE_ATTRIBUTE_HIDDEN;
586
0
  }
587
588
0
  return result;
589
0
}
590
591
/****************************************************************************
592
 Change a unix mode to a dos mode for an ms dfs link.
593
****************************************************************************/
594
595
uint32_t dos_mode_msdfs(connection_struct *conn,
596
      const char *name,
597
      const struct stat_ex *st)
598
0
{
599
0
  uint32_t result = 0;
600
601
0
  DEBUG(8, ("dos_mode_msdfs: %s\n", name));
602
603
0
  if (!VALID_STAT(*st)) {
604
0
    return 0;
605
0
  }
606
607
0
  result = dos_mode_from_name(conn, name, result);
608
0
  result |= dos_mode_from_sbuf(conn, st, NULL);
609
610
0
  if (result == 0) {
611
0
    result = FILE_ATTRIBUTE_NORMAL;
612
0
  }
613
614
0
  result = filter_mode_by_protocol(conn_protocol(conn->sconn), result);
615
616
  /*
617
   * Add in that it is a reparse point
618
   */
619
0
  result |= FILE_ATTRIBUTE_REPARSE_POINT;
620
621
0
  dos_mode_debug_print(__func__, result);
622
623
0
  return(result);
624
0
}
625
626
/*
627
 * check whether a file or directory is flagged as compressed.
628
 */
629
static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
630
            bool *is_compressed)
631
0
{
632
0
  NTSTATUS status;
633
0
  uint16_t compression_fmt;
634
635
0
  status = SMB_VFS_FGET_COMPRESSION(
636
0
    fsp->conn, talloc_tos(), fsp, &compression_fmt);
637
0
  if (!NT_STATUS_IS_OK(status)) {
638
0
    return status;
639
0
  }
640
641
0
  if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
642
0
    *is_compressed = true;
643
0
  } else {
644
0
    *is_compressed = false;
645
0
  }
646
0
  return NT_STATUS_OK;
647
0
}
648
649
static uint32_t dos_mode_post(uint32_t dosmode,
650
            struct files_struct *fsp,
651
            const char *func)
652
0
{
653
0
  struct smb_filename *smb_fname = NULL;
654
0
  NTSTATUS status;
655
656
0
  if (fsp != NULL) {
657
0
    smb_fname = fsp->fsp_name;
658
0
  }
659
0
  SMB_ASSERT(smb_fname != NULL);
660
661
  /*
662
   * According to MS-FSA a stream name does not have
663
   * separate DOS attribute metadata, so we must return
664
   * the DOS attribute from the base filename. With one caveat,
665
   * a non-default stream name can never be a directory.
666
   *
667
   * As this is common to all streams data stores, we handle
668
   * it here instead of inside all stream VFS modules.
669
   *
670
   * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
671
   */
672
673
0
  if (is_named_stream(smb_fname)) {
674
    /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
675
0
    dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
676
0
  }
677
678
0
  if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
679
0
    bool compressed = false;
680
681
0
    status = dos_mode_check_compressed(fsp, &compressed);
682
0
    if (NT_STATUS_IS_OK(status) && compressed) {
683
0
      dosmode |= FILE_ATTRIBUTE_COMPRESSED;
684
0
    }
685
0
  }
686
687
0
  dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
688
689
0
  if (S_ISDIR(smb_fname->st.st_ex_mode)) {
690
0
    if (!(dosmode & FILE_ATTRIBUTE_REPARSE_POINT)) {
691
0
      dosmode |= FILE_ATTRIBUTE_DIRECTORY;
692
0
    }
693
0
  } else if (dosmode == 0) {
694
0
    dosmode = FILE_ATTRIBUTE_NORMAL;
695
0
  }
696
697
0
  dosmode = filter_mode_by_protocol(conn_protocol(fsp->conn->sconn),
698
0
            dosmode);
699
700
0
  dos_mode_debug_print(func, dosmode);
701
0
  return dosmode;
702
0
}
703
704
/****************************************************************************
705
 Change a unix mode to a dos mode.
706
 May also read the create timespec into the stat struct in smb_fname
707
 if "store dos attributes" is true.
708
****************************************************************************/
709
710
uint32_t fdos_mode(struct files_struct *fsp)
711
0
{
712
0
  uint32_t result = 0;
713
0
  NTSTATUS status = NT_STATUS_OK;
714
715
0
  DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
716
717
0
  if (fsp->fake_file_handle != NULL) {
718
0
    return dosmode_from_fake_filehandle(fsp->fake_file_handle);
719
0
  }
720
721
0
  if (!VALID_STAT(fsp->fsp_name->st)) {
722
0
    return 0;
723
0
  }
724
725
0
  switch (fsp->fsp_name->st.st_ex_mode & S_IFMT) {
726
0
  case S_IFREG:
727
0
  case S_IFDIR:
728
0
    break;
729
0
  case S_IFLNK:
730
0
    if (fsp->fsp_flags.posix_open &&
731
0
        !conn_using_smb2(fsp->conn->sconn)) {
732
      /*
733
       * SMB1 posix doesn't like the reparse point flag
734
       */
735
0
      result = FILE_ATTRIBUTE_NORMAL;
736
0
    } else {
737
      /*
738
       * Everybody else wants to see symlinks as
739
       * reparse points
740
       */
741
0
      result = FILE_ATTRIBUTE_REPARSE_POINT;
742
0
    }
743
744
0
    break;
745
0
  default:
746
0
    return FILE_ATTRIBUTE_REPARSE_POINT;
747
0
    break;
748
0
  }
749
750
0
  if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTE_INVALID) {
751
0
    return fsp->fsp_name->st.cached_dos_attributes;
752
0
  }
753
754
  /* Get the DOS attributes via the VFS if we can */
755
0
  status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn,
756
0
               metadata_fsp(fsp),
757
0
               &result);
758
759
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
760
0
    result |= dos_mode_from_sbuf(fsp->conn,
761
0
               &fsp->fsp_name->st,
762
0
               fsp);
763
0
  }
764
765
0
  fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
766
0
  return fsp->fsp_name->st.cached_dos_attributes;
767
0
}
768
769
struct dos_mode_at_state {
770
  files_struct *dir_fsp;
771
  struct smb_filename *smb_fname;
772
  uint32_t dosmode;
773
};
774
775
static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
776
777
struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
778
            struct tevent_context *ev,
779
            files_struct *dir_fsp,
780
            struct smb_filename *smb_fname)
781
0
{
782
0
  struct tevent_req *req = NULL;
783
0
  struct dos_mode_at_state *state = NULL;
784
0
  struct tevent_req *subreq = NULL;
785
786
0
  DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
787
788
0
  req = tevent_req_create(mem_ctx, &state,
789
0
        struct dos_mode_at_state);
790
0
  if (req == NULL) {
791
0
    return NULL;
792
0
  }
793
794
0
  *state = (struct dos_mode_at_state) {
795
0
    .dir_fsp = dir_fsp,
796
0
    .smb_fname = smb_fname,
797
0
  };
798
799
0
  if (!VALID_STAT(smb_fname->st)) {
800
0
    tevent_req_done(req);
801
0
    return tevent_req_post(req, ev);
802
0
  }
803
804
0
  if (smb_fname->fsp == NULL) {
805
0
    if (ISDOTDOT(smb_fname->base_name)) {
806
      /*
807
       * smb_fname->fsp is explicitly closed
808
       * for ".." to prevent meta-data leakage.
809
       */
810
0
      state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
811
0
    } else {
812
      /*
813
       * This is a symlink in POSIX context.
814
       * FIXME ? Should we move to returning
815
       * FILE_ATTRIBUTE_REPARSE_POINT here ?
816
       */
817
0
      state->dosmode = FILE_ATTRIBUTE_NORMAL;
818
0
    }
819
0
    tevent_req_done(req);
820
0
    return tevent_req_post(req, ev);
821
0
  }
822
823
0
  subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
824
0
             ev,
825
0
             dir_fsp,
826
0
             smb_fname);
827
0
  if (tevent_req_nomem(subreq, req)) {
828
0
    return tevent_req_post(req, ev);
829
0
  }
830
0
  tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
831
832
0
  return req;
833
0
}
834
835
static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
836
0
{
837
0
  struct tevent_req *req =
838
0
    tevent_req_callback_data(subreq,
839
0
    struct tevent_req);
840
0
  struct dos_mode_at_state *state =
841
0
    tevent_req_data(req,
842
0
    struct dos_mode_at_state);
843
0
  struct vfs_aio_state aio_state;
844
0
  NTSTATUS status;
845
0
  bool ok;
846
847
  /*
848
   * Make sure we run as the user again
849
   */
850
0
  ok = change_to_user_and_service_by_fsp(state->dir_fsp);
851
0
  SMB_ASSERT(ok);
852
853
0
  status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
854
0
             &aio_state,
855
0
             &state->dosmode);
856
0
  TALLOC_FREE(subreq);
857
0
  if (!NT_STATUS_IS_OK(status)) {
858
    /*
859
     * Both the sync dos_mode() as well as the async
860
     * dos_mode_at_[send|recv] have no real error return, the only
861
     * unhandled error is when the stat info in smb_fname is not
862
     * valid (cf the checks in dos_mode() and dos_mode_at_send().
863
     *
864
     * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
865
     * dos_mode_post() which also does the mapping of a last resort
866
     * from S_IFMT(st_mode).
867
     *
868
     * Only if we get NT_STATUS_NOT_IMPLEMENTED or
869
     * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
870
     * fallback to sync processing.
871
     */
872
0
    if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
873
0
        !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
874
0
    {
875
      /*
876
       * state->dosmode should still be 0, but reset
877
       * it to be sure.
878
       */
879
0
      state->dosmode = 0;
880
0
      status = NT_STATUS_OK;
881
0
    }
882
0
  }
883
0
  if (NT_STATUS_IS_OK(status)) {
884
0
    state->dosmode = dos_mode_post(state->dosmode,
885
0
                 state->smb_fname->fsp,
886
0
                 __func__);
887
0
    tevent_req_done(req);
888
0
    return;
889
0
  }
890
891
  /*
892
   * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
893
   */
894
895
0
  state->dosmode = fdos_mode(state->smb_fname->fsp);
896
0
  tevent_req_done(req);
897
0
  return;
898
0
}
899
900
NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
901
0
{
902
0
  struct dos_mode_at_state *state =
903
0
    tevent_req_data(req,
904
0
    struct dos_mode_at_state);
905
0
  NTSTATUS status;
906
907
0
  if (tevent_req_is_nterror(req, &status)) {
908
0
    tevent_req_received(req);
909
0
    return status;
910
0
  }
911
912
0
  *dosmode = state->dosmode;
913
0
  tevent_req_received(req);
914
0
  return NT_STATUS_OK;
915
0
}
916
917
/*******************************************************************
918
 chmod a file - but preserve some bits.
919
 If "store dos attributes" is also set it will store the create time
920
 from the stat struct in smb_fname (in NTTIME format) in the EA
921
 attribute also.
922
********************************************************************/
923
924
int file_set_dosmode(connection_struct *conn,
925
         struct smb_filename *smb_fname,
926
         uint32_t dosmode,
927
         struct files_struct *dirfsp,
928
         bool newfile)
929
0
{
930
0
  int mask=0;
931
0
  mode_t tmp;
932
0
  mode_t unixmode;
933
0
  int ret = -1;
934
0
  NTSTATUS status;
935
936
0
  if (!CAN_WRITE(conn)) {
937
0
    errno = EROFS;
938
0
    return -1;
939
0
  }
940
941
0
  if (S_ISLNK(smb_fname->st.st_ex_mode)) {
942
    /* A symlink in POSIX context, ignore */
943
0
    return 0;
944
0
  }
945
946
0
  if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
947
0
      (dosmode & FILE_ATTRIBUTE_TEMPORARY))
948
0
  {
949
0
    errno = EINVAL;
950
0
    return -1;
951
0
  }
952
953
0
  dosmode &= SAMBA_ATTRIBUTES_MASK;
954
955
0
  DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
956
0
      dosmode, smb_fname_str_dbg(smb_fname)));
957
958
0
  if (smb_fname->fsp == NULL) {
959
0
    errno = ENOENT;
960
0
    return -1;
961
0
  }
962
963
0
  if (smb_fname->fsp->fsp_flags.posix_open &&
964
0
      !lp_store_dos_attributes(SNUM(conn)))
965
0
  {
966
0
    return 0;
967
0
  }
968
969
0
  unixmode = smb_fname->st.st_ex_mode;
970
971
0
  get_acl_group_bits(conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
972
973
0
  if (S_ISDIR(smb_fname->st.st_ex_mode))
974
0
    dosmode |= FILE_ATTRIBUTE_DIRECTORY;
975
0
  else
976
0
    dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
977
978
  /* Store the DOS attributes in an EA by preference. */
979
0
  status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
980
0
               metadata_fsp(smb_fname->fsp),
981
0
               dosmode);
982
0
  if (NT_STATUS_IS_OK(status)) {
983
0
    smb_fname->st.cached_dos_attributes = dosmode;
984
0
    ret = 0;
985
0
    goto done;
986
0
  }
987
988
  /*
989
   * Only fall back to using UNIX modes if
990
   * we get NOT_IMPLEMENTED.
991
   */
992
0
  if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
993
0
    errno = map_errno_from_nt_status(status);
994
0
    return -1;
995
0
  }
996
997
  /* Fall back to UNIX modes. */
998
0
  unixmode = unix_mode(conn, dosmode, smb_fname, dirfsp);
999
1000
  /* preserve the file type bits */
1001
0
  mask |= S_IFMT;
1002
1003
  /* preserve the s bits */
1004
0
  mask |= (S_ISUID | S_ISGID);
1005
1006
  /* preserve the t bit */
1007
0
#ifdef S_ISVTX
1008
0
  mask |= S_ISVTX;
1009
0
#endif
1010
1011
  /* possibly preserve the x bits */
1012
0
  if (!MAP_ARCHIVE(conn))
1013
0
    mask |= S_IXUSR;
1014
0
  if (!MAP_SYSTEM(conn))
1015
0
    mask |= S_IXGRP;
1016
0
  if (!MAP_HIDDEN(conn))
1017
0
    mask |= S_IXOTH;
1018
1019
0
  unixmode |= (smb_fname->st.st_ex_mode & mask);
1020
1021
  /* if we previously had any r bits set then leave them alone */
1022
0
  if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
1023
0
    unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
1024
0
    unixmode |= tmp;
1025
0
  }
1026
1027
  /* if we previously had any w bits set then leave them alone
1028
    whilst adding in the new w bits, if the new mode is not rdonly */
1029
0
  if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
1030
0
    unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1031
0
  }
1032
1033
  /*
1034
   * From the chmod 2 man page:
1035
   *
1036
   * "If the calling process is not privileged, and the group of the file
1037
   * does not match the effective group ID of the process or one of its
1038
   * supplementary group IDs, the S_ISGID bit will be turned off, but
1039
   * this will not cause an error to be returned."
1040
   *
1041
   * Simply refuse to do the chmod in this case.
1042
   */
1043
1044
0
  if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1045
0
      (unixmode & S_ISGID) &&
1046
0
      geteuid() != sec_initial_uid() &&
1047
0
      !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1048
0
  {
1049
0
    DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1050
0
      "set for directory %s\n",
1051
0
      smb_fname_str_dbg(smb_fname)));
1052
0
    errno = EPERM;
1053
0
    return -1;
1054
0
  }
1055
1056
0
  ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1057
0
  if (ret == 0) {
1058
0
    goto done;
1059
0
  }
1060
1061
0
  if((errno != EPERM) && (errno != EACCES))
1062
0
    return -1;
1063
1064
0
  if(!lp_dos_filemode(SNUM(conn)))
1065
0
    return -1;
1066
1067
  /* We want DOS semantics, ie allow non owner with write permission to change the
1068
    bits on a file. Just like file_ntimes below.
1069
  */
1070
1071
0
  if (!can_write_to_fsp(smb_fname->fsp))
1072
0
  {
1073
0
    errno = EACCES;
1074
0
    return -1;
1075
0
  }
1076
1077
0
  become_root();
1078
0
  ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1079
0
  unbecome_root();
1080
1081
0
done:
1082
0
  if (!newfile) {
1083
0
    notify_fname(conn,
1084
0
           NOTIFY_ACTION_MODIFIED |
1085
0
           NOTIFY_ACTION_DIRLEASE_BREAK,
1086
0
           FILE_NOTIFY_CHANGE_ATTRIBUTES,
1087
0
           smb_fname,
1088
0
           fsp_get_smb2_lease(smb_fname->fsp));
1089
0
  }
1090
0
  if (ret == 0) {
1091
0
    smb_fname->st.st_ex_mode = unixmode;
1092
0
  }
1093
1094
0
  return( ret );
1095
0
}
1096
1097
1098
NTSTATUS file_set_sparse(connection_struct *conn,
1099
       files_struct *fsp,
1100
       bool sparse)
1101
0
{
1102
0
  const struct loadparm_substitution *lp_sub =
1103
0
    loadparm_s3_global_substitution();
1104
0
  uint32_t old_dosmode;
1105
0
  uint32_t new_dosmode;
1106
0
  NTSTATUS status;
1107
1108
0
  if (!CAN_WRITE(conn)) {
1109
0
    DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1110
0
      "on readonly share[%s]\n",
1111
0
      smb_fname_str_dbg(fsp->fsp_name),
1112
0
      sparse,
1113
0
      lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1114
0
    return NT_STATUS_MEDIA_WRITE_PROTECTED;
1115
0
  }
1116
1117
  /*
1118
   * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1119
   * following access flags are granted.
1120
   */
1121
0
  status = check_any_access_fsp(fsp,
1122
0
          FILE_WRITE_DATA
1123
0
          | FILE_WRITE_ATTRIBUTES
1124
0
          | SEC_FILE_APPEND_DATA);
1125
0
  if (!NT_STATUS_IS_OK(status)) {
1126
0
    DBG_DEBUG("fname[%s] set[%u] "
1127
0
        "access_mask[0x%08X] - access denied\n",
1128
0
        smb_fname_str_dbg(fsp->fsp_name),
1129
0
        sparse,
1130
0
        fsp->access_mask);
1131
0
    return status;
1132
0
  }
1133
1134
0
  if (fsp->fsp_flags.is_directory) {
1135
0
    DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1136
0
        (sparse ? "set" : "clear"),
1137
0
        smb_fname_str_dbg(fsp->fsp_name)));
1138
0
    return NT_STATUS_INVALID_PARAMETER;
1139
0
  }
1140
1141
0
  if (IS_IPC(conn) || IS_PRINT(conn)) {
1142
0
    DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1143
0
        (sparse ? "set" : "clear")));
1144
0
    return NT_STATUS_INVALID_PARAMETER;
1145
0
  }
1146
1147
0
  if (fsp_is_alternate_stream(fsp)) {
1148
    /*
1149
     * MS-FSA 2.1.1.5 IsSparse
1150
     *
1151
     * This is a per stream attribute, but our backends don't
1152
     * support it a consistent way, therefore just pretend
1153
     * success and ignore the request.
1154
     */
1155
0
    DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1156
0
        "[%s]\n", fsp_str_dbg(fsp));
1157
0
    return NT_STATUS_OK;
1158
0
  }
1159
1160
0
  DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1161
0
      sparse, smb_fname_str_dbg(fsp->fsp_name)));
1162
1163
0
  if (!lp_store_dos_attributes(SNUM(conn))) {
1164
0
    return NT_STATUS_INVALID_DEVICE_REQUEST;
1165
0
  }
1166
1167
0
  status = vfs_stat_fsp(fsp);
1168
0
  if (!NT_STATUS_IS_OK(status)) {
1169
0
    return status;
1170
0
  }
1171
1172
0
  old_dosmode = fdos_mode(fsp);
1173
1174
0
  if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1175
0
    new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1176
0
  } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1177
0
    new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1178
0
  } else {
1179
0
    return NT_STATUS_OK;
1180
0
  }
1181
1182
  /* Store the DOS attributes in an EA. */
1183
0
  status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1184
0
  if (!NT_STATUS_IS_OK(status)) {
1185
0
    return status;
1186
0
  }
1187
1188
0
  notify_fname(conn,
1189
0
         NOTIFY_ACTION_MODIFIED |
1190
0
         NOTIFY_ACTION_DIRLEASE_BREAK,
1191
0
         FILE_NOTIFY_CHANGE_ATTRIBUTES,
1192
0
         fsp->fsp_name,
1193
0
         fsp_get_smb2_lease(fsp));
1194
1195
0
  fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1196
0
  fsp->fsp_flags.is_sparse = sparse;
1197
1198
0
  return NT_STATUS_OK;
1199
0
}
1200
1201
/*******************************************************************
1202
 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1203
 than POSIX.
1204
*******************************************************************/
1205
1206
int file_ntimes(connection_struct *conn,
1207
    files_struct *fsp,
1208
    struct smb_file_time *ft)
1209
0
{
1210
0
  int ret = -1;
1211
1212
0
  errno = 0;
1213
1214
0
  DBG_INFO("actime: %s",
1215
0
     time_to_asc(convert_timespec_to_time_t(ft->atime)));
1216
0
  DBG_INFO("modtime: %s",
1217
0
     time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1218
0
  DBG_INFO("ctime: %s",
1219
0
     time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1220
0
  DBG_INFO("createtime: %s",
1221
0
     time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1222
1223
  /* Don't update the time on read-only shares */
1224
  /* We need this as set_filetime (which can be called on
1225
     close and other paths) can end up calling this function
1226
     without the NEED_WRITE protection. Found by :
1227
     Leo Weppelman <leo@wau.mis.ah.nl>
1228
  */
1229
1230
0
  if (!CAN_WRITE(conn)) {
1231
0
    return 0;
1232
0
  }
1233
1234
0
  if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1235
0
    ret = 0;
1236
0
    goto done;
1237
0
  }
1238
1239
0
  if((errno != EPERM) && (errno != EACCES)) {
1240
0
    return -1;
1241
0
  }
1242
1243
0
  if(!lp_dos_filetimes(SNUM(conn))) {
1244
0
    return -1;
1245
0
  }
1246
1247
  /* We have permission (given by the Samba admin) to
1248
     break POSIX semantics and allow a user to change
1249
     the time on a file they don't own but can write to
1250
     (as DOS does).
1251
   */
1252
1253
  /* Check if we have write access. */
1254
0
  if (can_write_to_fsp(fsp)) {
1255
    /* We are allowed to become root and change the filetime. */
1256
0
    become_root();
1257
0
    ret = SMB_VFS_FNTIMES(fsp, ft);
1258
0
    unbecome_root();
1259
0
  }
1260
1261
0
done:
1262
0
  if (ret == 0) {
1263
0
    copy_stat_ex_timestamps(&fsp->fsp_name->st, ft);
1264
0
  }
1265
1266
0
  return ret;
1267
0
}
1268
1269
/******************************************************************
1270
 Force a "sticky" write time on an fsp. This will always be
1271
 returned on all future write time queries and set on close.
1272
******************************************************************/
1273
1274
void set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1275
0
{
1276
0
  if (fsp->fsp_flags.posix_open) {
1277
0
    return;
1278
0
  }
1279
0
  if (is_omit_timespec(&mtime)) {
1280
0
    return;
1281
0
  }
1282
1283
0
  fsp->fsp_flags.write_time_forced = true;
1284
0
}
1285
1286
/******************************************************************
1287
 Set a create time EA.
1288
******************************************************************/
1289
1290
NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1291
        struct timespec create_time)
1292
0
{
1293
0
  uint32_t dosmode;
1294
0
  int ret;
1295
1296
0
  if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1297
0
    return NT_STATUS_OK;
1298
0
  }
1299
1300
0
  dosmode = fdos_mode(fsp);
1301
1302
0
  fsp->fsp_name->st.st_ex_btime = create_time;
1303
0
  ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1304
0
  if (ret == -1) {
1305
0
    return map_nt_error_from_unix(errno);
1306
0
  }
1307
1308
0
  DBG_DEBUG("wrote create time EA for file %s\n",
1309
0
    smb_fname_str_dbg(fsp->fsp_name));
1310
1311
0
  return NT_STATUS_OK;
1312
0
}
1313
1314
/******************************************************************
1315
 Return a create time.
1316
******************************************************************/
1317
1318
struct timespec get_create_timespec(connection_struct *conn,
1319
        struct files_struct *fsp,
1320
        const struct smb_filename *smb_fname)
1321
0
{
1322
0
  if (fsp != NULL) {
1323
0
    struct files_struct *meta_fsp = metadata_fsp(fsp);
1324
0
    return meta_fsp->fsp_name->st.st_ex_btime;
1325
0
  }
1326
0
  return smb_fname->st.st_ex_btime;
1327
0
}