smbd: Re-register notify requests
[vlendec/samba-autobuild/.git] / source3 / smbd / dosmode.c
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 "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "librpc/gen_ndr/ioctl.h"
25 #include "../libcli/security/security.h"
26 #include "smbd/smbd.h"
27 #include "lib/param/loadparm.h"
28
29 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
30                                 const struct smb_filename *smb_fname,
31                                 files_struct **ret_fsp,
32                                 bool *need_close);
33
34 static void dos_mode_debug_print(const char *func, uint32_t mode)
35 {
36         fstring modestr;
37
38         if (DEBUGLEVEL < DBGLVL_INFO) {
39                 return;
40         }
41
42         modestr[0] = '\0';
43
44         if (mode & FILE_ATTRIBUTE_HIDDEN) {
45                 fstrcat(modestr, "h");
46         }
47         if (mode & FILE_ATTRIBUTE_READONLY) {
48                 fstrcat(modestr, "r");
49         }
50         if (mode & FILE_ATTRIBUTE_SYSTEM) {
51                 fstrcat(modestr, "s");
52         }
53         if (mode & FILE_ATTRIBUTE_DIRECTORY) {
54                 fstrcat(modestr, "d");
55         }
56         if (mode & FILE_ATTRIBUTE_ARCHIVE) {
57                 fstrcat(modestr, "a");
58         }
59         if (mode & FILE_ATTRIBUTE_SPARSE) {
60                 fstrcat(modestr, "[sparse]");
61         }
62         if (mode & FILE_ATTRIBUTE_OFFLINE) {
63                 fstrcat(modestr, "[offline]");
64         }
65         if (mode & FILE_ATTRIBUTE_COMPRESSED) {
66                 fstrcat(modestr, "[compressed]");
67         }
68
69         DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
70                  modestr);
71 }
72
73 static uint32_t filter_mode_by_protocol(uint32_t mode)
74 {
75         if (get_Protocol() <= PROTOCOL_LANMAN2) {
76                 DEBUG(10,("filter_mode_by_protocol: "
77                         "filtering result 0x%x to 0x%x\n",
78                         (unsigned int)mode,
79                         (unsigned int)(mode & 0x3f) ));
80                 mode &= 0x3f;
81         }
82         return mode;
83 }
84
85 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
86 {
87 #ifdef S_ISLNK
88 #if LINKS_READ_ONLY
89         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
90                 return FILE_ATTRIBUTE_READONLY;
91 #endif
92 #endif
93         return 0;
94 }
95
96 /****************************************************************************
97  Change a dos mode to a unix mode.
98     Base permission for files:
99          if creating file and inheriting (i.e. parent_dir != NULL)
100            apply read/write bits from parent directory.
101          else   
102            everybody gets read bit set
103          dos readonly is represented in unix by removing everyone's write bit
104          dos archive is represented in unix by the user's execute bit
105          dos system is represented in unix by the group's execute bit
106          dos hidden is represented in unix by the other's execute bit
107          if !inheriting {
108            Then apply create mask,
109            then add force bits.
110          }
111     Base permission for directories:
112          dos directory is represented in unix by unix's dir bit and the exec bit
113          if !inheriting {
114            Then apply create mask,
115            then add force bits.
116          }
117 ****************************************************************************/
118
119 mode_t unix_mode(connection_struct *conn, int dosmode,
120                  const struct smb_filename *smb_fname,
121                  const char *inherit_from_dir)
122 {
123         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
124         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
125                               * inheriting. */
126
127         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
128                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
129         }
130
131         if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) {
132                 struct smb_filename *smb_fname_parent;
133
134                 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
135                           smb_fname_str_dbg(smb_fname),
136                           inherit_from_dir));
137
138                 smb_fname_parent = synthetic_smb_fname(talloc_tos(),
139                                         inherit_from_dir,
140                                         NULL,
141                                         NULL,
142                                         smb_fname->flags);
143                 if (smb_fname_parent == NULL) {
144                         DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
145                                  smb_fname_str_dbg(smb_fname),
146                                  inherit_from_dir));
147                         return(0);
148                 }
149
150                 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
151                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
152                                  smb_fname_str_dbg(smb_fname),
153                                  inherit_from_dir, strerror(errno)));
154                         TALLOC_FREE(smb_fname_parent);
155                         return(0);      /* *** shouldn't happen! *** */
156                 }
157
158                 /* Save for later - but explicitly remove setuid bit for safety. */
159                 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
160                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
161                          smb_fname_str_dbg(smb_fname), (int)dir_mode));
162                 /* Clear "result" */
163                 result = 0;
164                 TALLOC_FREE(smb_fname_parent);
165         } 
166
167         if (IS_DOS_DIR(dosmode)) {
168                 /* We never make directories read only for the owner as under DOS a user
169                 can always create a file in a read-only directory. */
170                 result |= (S_IFDIR | S_IWUSR);
171
172                 if (dir_mode) {
173                         /* Inherit mode of parent directory. */
174                         result |= dir_mode;
175                 } else {
176                         /* Provisionally add all 'x' bits */
177                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
178
179                         /* Apply directory mask */
180                         result &= lp_directory_mask(SNUM(conn));
181                         /* Add in force bits */
182                         result |= lp_force_directory_mode(SNUM(conn));
183                 }
184         } else { 
185                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
186                         result |= S_IXUSR;
187
188                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
189                         result |= S_IXGRP;
190
191                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
192                         result |= S_IXOTH;  
193
194                 if (dir_mode) {
195                         /* Inherit 666 component of parent directory mode */
196                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
197                 } else {
198                         /* Apply mode mask */
199                         result &= lp_create_mask(SNUM(conn));
200                         /* Add in force bits */
201                         result |= lp_force_create_mode(SNUM(conn));
202                 }
203         }
204
205         DBG_INFO("unix_mode(%s) returning 0%o\n",
206                  smb_fname_str_dbg(smb_fname), (int)result);
207
208         return(result);
209 }
210
211 /****************************************************************************
212  Change a unix mode to a dos mode.
213 ****************************************************************************/
214
215 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
216                                  const struct smb_filename *smb_fname)
217 {
218         int result = 0;
219         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
220
221 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
222         /* if we can find out if a file is immutable we should report it r/o */
223         if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
224                 result |= FILE_ATTRIBUTE_READONLY;
225         }
226 #endif
227         if (ro_opts == MAP_READONLY_YES) {
228                 /* Original Samba method - map inverse of user "w" bit. */
229                 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
230                         result |= FILE_ATTRIBUTE_READONLY;
231                 }
232         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
233                 /* Check actual permissions for read-only. */
234                 if (!can_write_to_file(conn, smb_fname)) {
235                         result |= FILE_ATTRIBUTE_READONLY;
236                 }
237         } /* Else never set the readonly bit. */
238
239         if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
240                 result |= FILE_ATTRIBUTE_ARCHIVE;
241
242         if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
243                 result |= FILE_ATTRIBUTE_SYSTEM;
244
245         if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
246                 result |= FILE_ATTRIBUTE_HIDDEN;
247
248         if (S_ISDIR(smb_fname->st.st_ex_mode))
249                 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
250
251         result |= set_link_read_only_flag(&smb_fname->st);
252
253         dos_mode_debug_print(__func__, result);
254
255         return result;
256 }
257
258 /****************************************************************************
259  Get DOS attributes from an EA.
260  This can also pull the create time into the stat struct inside smb_fname.
261 ****************************************************************************/
262
263 NTSTATUS get_ea_dos_attribute(connection_struct *conn,
264                               struct smb_filename *smb_fname,
265                               uint32_t *pattr)
266 {
267         struct xattr_DOSATTRIB dosattrib;
268         enum ndr_err_code ndr_err;
269         DATA_BLOB blob;
270         ssize_t sizeret;
271         fstring attrstr;
272         uint32_t dosattr;
273
274         if (!lp_store_dos_attributes(SNUM(conn))) {
275                 return NT_STATUS_NOT_IMPLEMENTED;
276         }
277
278         /* Don't reset pattr to zero as we may already have filename-based attributes we
279            need to preserve. */
280
281         sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
282                                    SAMBA_XATTR_DOS_ATTRIB, attrstr,
283                                    sizeof(attrstr));
284         if (sizeret == -1) {
285                 DBG_INFO("Cannot get attribute "
286                          "from EA on file %s: Error = %s\n",
287                          smb_fname_str_dbg(smb_fname), strerror(errno));
288                 return map_nt_error_from_unix(errno);
289         }
290
291         blob.data = (uint8_t *)attrstr;
292         blob.length = sizeret;
293
294         ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
295                         (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
296
297         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
298                 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
299                          "from EA on file %s: Error = %s\n",
300                          smb_fname_str_dbg(smb_fname),
301                          ndr_errstr(ndr_err)));
302                 return ndr_map_error2ntstatus(ndr_err);
303         }
304
305         DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
306                   smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
307
308         switch (dosattrib.version) {
309                 case 0xFFFF:
310                         dosattr = dosattrib.info.compatinfoFFFF.attrib;
311                         break;
312                 case 1:
313                         dosattr = dosattrib.info.info1.attrib;
314                         if (!null_nttime(dosattrib.info.info1.create_time)) {
315                                 struct timespec create_time =
316                                         nt_time_to_unix_timespec(
317                                                 dosattrib.info.info1.create_time);
318
319                                 update_stat_ex_create_time(&smb_fname->st,
320                                                         create_time);
321
322                                 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
323                                         "set btime %s\n",
324                                         smb_fname_str_dbg(smb_fname),
325                                         time_to_asc(convert_timespec_to_time_t(
326                                                 create_time)) ));
327                         }
328                         break;
329                 case 2:
330                         dosattr = dosattrib.info.oldinfo2.attrib;
331                         /* Don't know what flags to check for this case. */
332                         break;
333                 case 3:
334                         dosattr = dosattrib.info.info3.attrib;
335                         if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
336                                         !null_nttime(dosattrib.info.info3.create_time)) {
337                                 struct timespec create_time =
338                                         nt_time_to_unix_timespec(
339                                                 dosattrib.info.info3.create_time);
340
341                                 update_stat_ex_create_time(&smb_fname->st,
342                                                         create_time);
343
344                                 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
345                                         "set btime %s\n",
346                                         smb_fname_str_dbg(smb_fname),
347                                         time_to_asc(convert_timespec_to_time_t(
348                                                 create_time)) ));
349                         }
350                         break;
351                 default:
352                         DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
353                                  "file %s - %s\n", smb_fname_str_dbg(smb_fname),
354                                  attrstr));
355                         /* Should this be INTERNAL_ERROR? */
356                         return NT_STATUS_INVALID_PARAMETER;
357         }
358
359         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
360                 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
361         }
362         /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
363         *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
364
365         dos_mode_debug_print(__func__, *pattr);
366
367         return NT_STATUS_OK;
368 }
369
370 /****************************************************************************
371  Set DOS attributes in an EA.
372  Also sets the create time.
373 ****************************************************************************/
374
375 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
376                               const struct smb_filename *smb_fname,
377                               uint32_t dosmode)
378 {
379         struct xattr_DOSATTRIB dosattrib;
380         enum ndr_err_code ndr_err;
381         DATA_BLOB blob;
382
383         if (!lp_store_dos_attributes(SNUM(conn))) {
384                 return NT_STATUS_NOT_IMPLEMENTED;
385         }
386
387         ZERO_STRUCT(dosattrib);
388         ZERO_STRUCT(blob);
389
390         dosattrib.version = 3;
391         dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
392                                         XATTR_DOSINFO_CREATE_TIME;
393         dosattrib.info.info3.attrib = dosmode;
394         dosattrib.info.info3.create_time = unix_timespec_to_nt_time(
395                                 smb_fname->st.st_ex_btime);
396
397         DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
398                 (unsigned int)dosmode,
399                 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
400                 smb_fname_str_dbg(smb_fname) ));
401
402         ndr_err = ndr_push_struct_blob(
403                         &blob, talloc_tos(), &dosattrib,
404                         (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
405
406         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
407                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
408                         ndr_errstr(ndr_err)));
409                 return ndr_map_error2ntstatus(ndr_err);
410         }
411
412         if (blob.data == NULL || blob.length == 0) {
413                 /* Should this be INTERNAL_ERROR? */
414                 return NT_STATUS_INVALID_PARAMETER;
415         }
416
417         if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
418                              SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
419                              0) == -1) {
420                 NTSTATUS status = NT_STATUS_OK;
421                 bool need_close = false;
422                 files_struct *fsp = NULL;
423
424                 if((errno != EPERM) && (errno != EACCES)) {
425                         DBG_INFO("Cannot set "
426                                  "attribute EA on file %s: Error = %s\n",
427                                  smb_fname_str_dbg(smb_fname), strerror(errno));
428                         return map_nt_error_from_unix(errno);
429                 }
430
431                 /* We want DOS semantics, ie allow non owner with write permission to change the
432                         bits on a file. Just like file_ntimes below.
433                 */
434
435                 /* Check if we have write access. */
436                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
437                         return NT_STATUS_ACCESS_DENIED;
438
439                 if (!can_write_to_file(conn, smb_fname)) {
440                         return NT_STATUS_ACCESS_DENIED;
441                 }
442
443                 /*
444                  * We need to get an open file handle to do the
445                  * metadata operation under root.
446                  */
447
448                 status = get_file_handle_for_metadata(conn,
449                                                 smb_fname,
450                                                 &fsp,
451                                                 &need_close);
452                 if (!NT_STATUS_IS_OK(status)) {
453                         return status;
454                 }
455
456                 become_root();
457                 if (SMB_VFS_FSETXATTR(fsp,
458                                      SAMBA_XATTR_DOS_ATTRIB, blob.data,
459                                      blob.length, 0) == 0) {
460                         status = NT_STATUS_OK;
461                 }
462                 unbecome_root();
463                 if (need_close) {
464                         close_file(NULL, fsp, NORMAL_CLOSE);
465                 }
466                 return status;
467         }
468         DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
469                 (unsigned int)dosmode,
470                 smb_fname_str_dbg(smb_fname)));
471         return NT_STATUS_OK;
472 }
473
474 /****************************************************************************
475  Change a unix mode to a dos mode for an ms dfs link.
476 ****************************************************************************/
477
478 uint32_t dos_mode_msdfs(connection_struct *conn,
479                       const struct smb_filename *smb_fname)
480 {
481         uint32_t result = 0;
482
483         DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
484
485         if (!VALID_STAT(smb_fname->st)) {
486                 return 0;
487         }
488
489         /* First do any modifications that depend on the path name. */
490         /* hide files with a name starting with a . */
491         if (lp_hide_dot_files(SNUM(conn))) {
492                 const char *p = strrchr_m(smb_fname->base_name, '/');
493                 if (p) {
494                         p++;
495                 } else {
496                         p = smb_fname->base_name;
497                 }
498
499                 /* Only . and .. are not hidden. */
500                 if (p[0] == '.' && !((p[1] == '\0') ||
501                                 (p[1] == '.' && p[2] == '\0'))) {
502                         result |= FILE_ATTRIBUTE_HIDDEN;
503                 }
504         }
505
506         result |= dos_mode_from_sbuf(conn, smb_fname);
507
508         /* Optimization : Only call is_hidden_path if it's not already
509            hidden. */
510         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
511             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
512                 result |= FILE_ATTRIBUTE_HIDDEN;
513         }
514
515         if (result == 0) {
516                 result = FILE_ATTRIBUTE_NORMAL;
517         }
518
519         result = filter_mode_by_protocol(result);
520
521         /*
522          * Add in that it is a reparse point
523          */
524         result |= FILE_ATTRIBUTE_REPARSE_POINT;
525
526         dos_mode_debug_print(__func__, result);
527
528         return(result);
529 }
530
531 /*
532  * check whether a file or directory is flagged as compressed.
533  */
534 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
535                                           struct smb_filename *smb_fname,
536                                           bool *is_compressed)
537 {
538         NTSTATUS status;
539         uint16_t compression_fmt;
540         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
541         if (tmp_ctx == NULL) {
542                 status = NT_STATUS_NO_MEMORY;
543                 goto err_out;
544         }
545
546         status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
547                                          &compression_fmt);
548         if (!NT_STATUS_IS_OK(status)) {
549                 goto err_ctx_free;
550         }
551
552         if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
553                 *is_compressed = true;
554         } else {
555                 *is_compressed = false;
556         }
557         status = NT_STATUS_OK;
558
559 err_ctx_free:
560         talloc_free(tmp_ctx);
561 err_out:
562         return status;
563 }
564
565 static uint32_t dos_mode_from_name(connection_struct *conn,
566                                    const struct smb_filename *smb_fname,
567                                    uint32_t dosmode)
568 {
569         const char *p = NULL;
570         uint32_t result = dosmode;
571
572         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
573             lp_hide_dot_files(SNUM(conn)))
574         {
575                 p = strrchr_m(smb_fname->base_name, '/');
576                 if (p) {
577                         p++;
578                 } else {
579                         p = smb_fname->base_name;
580                 }
581
582                 /* Only . and .. are not hidden. */
583                 if ((p[0] == '.') &&
584                     !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
585                 {
586                         result |= FILE_ATTRIBUTE_HIDDEN;
587                 }
588         }
589
590         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
591             IS_HIDDEN_PATH(conn, smb_fname->base_name))
592         {
593                 result |= FILE_ATTRIBUTE_HIDDEN;
594         }
595
596         return result;
597 }
598
599 /****************************************************************************
600  Change a unix mode to a dos mode.
601  May also read the create timespec into the stat struct in smb_fname
602  if "store dos attributes" is true.
603 ****************************************************************************/
604
605 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
606 {
607         uint32_t result = 0;
608         bool offline;
609         NTSTATUS status = NT_STATUS_OK;
610
611         DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
612
613         if (!VALID_STAT(smb_fname->st)) {
614                 return 0;
615         }
616
617         /* Get the DOS attributes via the VFS if we can */
618         status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
619         if (!NT_STATUS_IS_OK(status)) {
620                 result |= dos_mode_from_sbuf(conn, smb_fname);
621         }
622
623         offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
624         if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
625                 result |= FILE_ATTRIBUTE_OFFLINE;
626         }
627
628         if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
629                 bool compressed = false;
630                 status = dos_mode_check_compressed(conn, smb_fname,
631                                                    &compressed);
632                 if (NT_STATUS_IS_OK(status) && compressed) {
633                         result |= FILE_ATTRIBUTE_COMPRESSED;
634                 }
635         }
636
637         result |= dos_mode_from_name(conn, smb_fname, result);
638
639         if (result == 0) {
640                 result = FILE_ATTRIBUTE_NORMAL;
641         }
642
643         result = filter_mode_by_protocol(result);
644
645         dos_mode_debug_print(__func__, result);
646
647         return result;
648 }
649
650 /*******************************************************************
651  chmod a file - but preserve some bits.
652  If "store dos attributes" is also set it will store the create time
653  from the stat struct in smb_fname (in NTTIME format) in the EA
654  attribute also.
655 ********************************************************************/
656
657 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
658                      uint32_t dosmode, const char *parent_dir, bool newfile)
659 {
660         int mask=0;
661         mode_t tmp;
662         mode_t unixmode;
663         int ret = -1, lret = -1;
664         uint32_t old_mode;
665         struct timespec new_create_timespec;
666         files_struct *fsp = NULL;
667         bool need_close = false;
668         NTSTATUS status;
669
670         if (!CAN_WRITE(conn)) {
671                 errno = EROFS;
672                 return -1;
673         }
674
675         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
676         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
677
678         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
679                   dosmode, smb_fname_str_dbg(smb_fname)));
680
681         unixmode = smb_fname->st.st_ex_mode;
682
683         get_acl_group_bits(conn, smb_fname->base_name,
684                            &smb_fname->st.st_ex_mode);
685
686         if (S_ISDIR(smb_fname->st.st_ex_mode))
687                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
688         else
689                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
690
691         new_create_timespec = smb_fname->st.st_ex_btime;
692
693         old_mode = dos_mode(conn, smb_fname);
694
695         if ((dosmode & FILE_ATTRIBUTE_OFFLINE) &&
696             !(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
697                 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
698                 if (lret == -1) {
699                         if (errno == ENOTSUP) {
700                                 DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
701                                            "%s/%s is not supported.\n",
702                                            parent_dir,
703                                            smb_fname_str_dbg(smb_fname)));
704                         } else {
705                                 DEBUG(0, ("An error occurred while setting "
706                                           "FILE_ATTRIBUTE_OFFLINE for "
707                                           "%s/%s: %s", parent_dir,
708                                           smb_fname_str_dbg(smb_fname),
709                                           strerror(errno)));
710                         }
711                 }
712         }
713
714         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
715         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
716
717         smb_fname->st.st_ex_btime = new_create_timespec;
718
719         /* Store the DOS attributes in an EA by preference. */
720         status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
721         if (NT_STATUS_IS_OK(status)) {
722                 if (!newfile) {
723                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
724                                 FILE_NOTIFY_CHANGE_ATTRIBUTES,
725                                 smb_fname->base_name);
726                 }
727                 smb_fname->st.st_ex_mode = unixmode;
728                 return 0;
729         } else {
730                 /*
731                  * Only fall back to using UNIX modes if
732                  * we get NOT_IMPLEMENTED.
733                  */
734                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
735                         errno = map_errno_from_nt_status(status);
736                         return -1;
737                 }
738         }
739
740         /* Fall back to UNIX modes. */
741         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
742
743         /* preserve the file type bits */
744         mask |= S_IFMT;
745
746         /* preserve the s bits */
747         mask |= (S_ISUID | S_ISGID);
748
749         /* preserve the t bit */
750 #ifdef S_ISVTX
751         mask |= S_ISVTX;
752 #endif
753
754         /* possibly preserve the x bits */
755         if (!MAP_ARCHIVE(conn))
756                 mask |= S_IXUSR;
757         if (!MAP_SYSTEM(conn))
758                 mask |= S_IXGRP;
759         if (!MAP_HIDDEN(conn))
760                 mask |= S_IXOTH;
761
762         unixmode |= (smb_fname->st.st_ex_mode & mask);
763
764         /* if we previously had any r bits set then leave them alone */
765         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
766                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
767                 unixmode |= tmp;
768         }
769
770         /* if we previously had any w bits set then leave them alone 
771                 whilst adding in the new w bits, if the new mode is not rdonly */
772         if (!IS_DOS_READONLY(dosmode)) {
773                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
774         }
775
776         /*
777          * From the chmod 2 man page:
778          *
779          * "If the calling process is not privileged, and the group of the file
780          * does not match the effective group ID of the process or one of its
781          * supplementary group IDs, the S_ISGID bit will be turned off, but
782          * this will not cause an error to be returned."
783          *
784          * Simply refuse to do the chmod in this case.
785          */
786
787         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
788                         geteuid() != sec_initial_uid() &&
789                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
790                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
791                         "set for directory %s\n",
792                         smb_fname_str_dbg(smb_fname)));
793                 errno = EPERM;
794                 return -1;
795         }
796
797         ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
798         if (ret == 0) {
799                 if(!newfile || (lret != -1)) {
800                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
801                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
802                                      smb_fname->base_name);
803                 }
804                 smb_fname->st.st_ex_mode = unixmode;
805                 return 0;
806         }
807
808         if((errno != EPERM) && (errno != EACCES))
809                 return -1;
810
811         if(!lp_dos_filemode(SNUM(conn)))
812                 return -1;
813
814         /* We want DOS semantics, ie allow non owner with write permission to change the
815                 bits on a file. Just like file_ntimes below.
816         */
817
818         if (!can_write_to_file(conn, smb_fname)) {
819                 errno = EACCES;
820                 return -1;
821         }
822
823         /*
824          * We need to get an open file handle to do the
825          * metadata operation under root.
826          */
827
828         status = get_file_handle_for_metadata(conn,
829                                               smb_fname,
830                                               &fsp,
831                                               &need_close);
832         if (!NT_STATUS_IS_OK(status)) {
833                 errno = map_errno_from_nt_status(status);
834                 return -1;
835         }
836
837         become_root();
838         ret = SMB_VFS_FCHMOD(fsp, unixmode);
839         unbecome_root();
840         if (need_close) {
841                 close_file(NULL, fsp, NORMAL_CLOSE);
842         }
843         if (!newfile) {
844                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
845                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
846                              smb_fname->base_name);
847         }
848         if (ret == 0) {
849                 smb_fname->st.st_ex_mode = unixmode;
850         }
851
852         return( ret );
853 }
854
855
856 NTSTATUS file_set_sparse(connection_struct *conn,
857                          files_struct *fsp,
858                          bool sparse)
859 {
860         uint32_t old_dosmode;
861         uint32_t new_dosmode;
862         NTSTATUS status;
863
864         if (!CAN_WRITE(conn)) {
865                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
866                         "on readonly share[%s]\n",
867                         smb_fname_str_dbg(fsp->fsp_name),
868                         sparse,
869                         lp_servicename(talloc_tos(), SNUM(conn))));
870                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
871         }
872
873         /*
874          * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
875          * following access flags are granted.
876          */
877         if ((fsp->access_mask & (FILE_WRITE_DATA
878                                 | FILE_WRITE_ATTRIBUTES
879                                 | SEC_FILE_APPEND_DATA)) == 0) {
880                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
881                         "access_mask[0x%08X] - access denied\n",
882                         smb_fname_str_dbg(fsp->fsp_name),
883                         sparse,
884                         fsp->access_mask));
885                 return NT_STATUS_ACCESS_DENIED;
886         }
887
888         if (fsp->is_directory) {
889                 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
890                           (sparse ? "set" : "clear"),
891                           smb_fname_str_dbg(fsp->fsp_name)));
892                 return NT_STATUS_INVALID_PARAMETER;
893         }
894
895         if (IS_IPC(conn) || IS_PRINT(conn)) {
896                 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
897                           (sparse ? "set" : "clear")));
898                 return NT_STATUS_INVALID_PARAMETER;
899         }
900
901         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
902                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
903
904         if (!lp_store_dos_attributes(SNUM(conn))) {
905                 return NT_STATUS_INVALID_DEVICE_REQUEST;
906         }
907
908         status = vfs_stat_fsp(fsp);
909         if (!NT_STATUS_IS_OK(status)) {
910                 return status;
911         }
912
913         old_dosmode = dos_mode(conn, fsp->fsp_name);
914
915         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
916                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
917         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
918                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
919         } else {
920                 return NT_STATUS_OK;
921         }
922
923         /* Store the DOS attributes in an EA. */
924         status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
925         if (!NT_STATUS_IS_OK(status)) {
926                 return status;
927         }
928
929         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
930                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
931                      fsp->fsp_name->base_name);
932
933         fsp->is_sparse = sparse;
934
935         return NT_STATUS_OK;
936 }
937
938 /*******************************************************************
939  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
940  than POSIX.
941 *******************************************************************/
942
943 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
944                 struct smb_file_time *ft)
945 {
946         int ret = -1;
947
948         errno = 0;
949
950         DEBUG(6, ("file_ntime: actime: %s",
951                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
952         DEBUG(6, ("file_ntime: modtime: %s",
953                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
954         DEBUG(6, ("file_ntime: ctime: %s",
955                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
956         DEBUG(6, ("file_ntime: createtime: %s",
957                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
958
959         /* Don't update the time on read-only shares */
960         /* We need this as set_filetime (which can be called on
961            close and other paths) can end up calling this function
962            without the NEED_WRITE protection. Found by : 
963            Leo Weppelman <leo@wau.mis.ah.nl>
964         */
965
966         if (!CAN_WRITE(conn)) {
967                 return 0;
968         }
969
970         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
971                 return 0;
972         }
973
974         if((errno != EPERM) && (errno != EACCES)) {
975                 return -1;
976         }
977
978         if(!lp_dos_filetimes(SNUM(conn))) {
979                 return -1;
980         }
981
982         /* We have permission (given by the Samba admin) to
983            break POSIX semantics and allow a user to change
984            the time on a file they don't own but can write to
985            (as DOS does).
986          */
987
988         /* Check if we have write access. */
989         if (can_write_to_file(conn, smb_fname)) {
990                 /* We are allowed to become root and change the filetime. */
991                 become_root();
992                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
993                 unbecome_root();
994         }
995
996         return ret;
997 }
998
999 /******************************************************************
1000  Force a "sticky" write time on a pathname. This will always be
1001  returned on all future write time queries and set on close.
1002 ******************************************************************/
1003
1004 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1005 {
1006         if (null_timespec(mtime)) {
1007                 return true;
1008         }
1009
1010         if (!set_sticky_write_time(fileid, mtime)) {
1011                 return false;
1012         }
1013
1014         return true;
1015 }
1016
1017 /******************************************************************
1018  Force a "sticky" write time on an fsp. This will always be
1019  returned on all future write time queries and set on close.
1020 ******************************************************************/
1021
1022 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1023 {
1024         if (null_timespec(mtime)) {
1025                 return true;
1026         }
1027
1028         fsp->write_time_forced = true;
1029         TALLOC_FREE(fsp->update_write_time_event);
1030
1031         return set_sticky_write_time_path(fsp->file_id, mtime);
1032 }
1033
1034 /******************************************************************
1035  Set a create time EA.
1036 ******************************************************************/
1037
1038 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1039                                 const struct smb_filename *psmb_fname,
1040                                 struct timespec create_time)
1041 {
1042         struct smb_filename *smb_fname;
1043         uint32_t dosmode;
1044         int ret;
1045
1046         if (!lp_store_dos_attributes(SNUM(conn))) {
1047                 return NT_STATUS_OK;
1048         }
1049
1050         smb_fname = synthetic_smb_fname(talloc_tos(),
1051                                         psmb_fname->base_name,
1052                                         NULL,
1053                                         &psmb_fname->st,
1054                                         psmb_fname->flags);
1055
1056         if (smb_fname == NULL) {
1057                 return NT_STATUS_NO_MEMORY;
1058         }
1059
1060         dosmode = dos_mode(conn, smb_fname);
1061
1062         smb_fname->st.st_ex_btime = create_time;
1063
1064         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1065         if (ret == -1) {
1066                 return map_nt_error_from_unix(errno);
1067         }
1068
1069         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1070                 smb_fname_str_dbg(smb_fname)));
1071
1072         return NT_STATUS_OK;
1073 }
1074
1075 /******************************************************************
1076  Return a create time.
1077 ******************************************************************/
1078
1079 struct timespec get_create_timespec(connection_struct *conn,
1080                                 struct files_struct *fsp,
1081                                 const struct smb_filename *smb_fname)
1082 {
1083         return smb_fname->st.st_ex_btime;
1084 }
1085
1086 /******************************************************************
1087  Return a change time (may look at EA in future).
1088 ******************************************************************/
1089
1090 struct timespec get_change_timespec(connection_struct *conn,
1091                                 struct files_struct *fsp,
1092                                 const struct smb_filename *smb_fname)
1093 {
1094         return smb_fname->st.st_ex_mtime;
1095 }
1096
1097 /****************************************************************************
1098  Get a real open file handle we can do meta-data operations on. As it's
1099  going to be used under root access only on meta-data we should look for
1100  any existing open file handle first, and use that in preference (also to
1101  avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1102 ****************************************************************************/
1103
1104 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1105                                 const struct smb_filename *smb_fname,
1106                                 files_struct **ret_fsp,
1107                                 bool *need_close)
1108 {
1109         NTSTATUS status;
1110         files_struct *fsp;
1111         struct file_id file_id;
1112         struct smb_filename *smb_fname_cp = NULL;
1113
1114         *need_close = false;
1115
1116         if (!VALID_STAT(smb_fname->st)) {
1117                 return NT_STATUS_INVALID_PARAMETER;
1118         }
1119
1120         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1121
1122         for(fsp = file_find_di_first(conn->sconn, file_id);
1123                         fsp;
1124                         fsp = file_find_di_next(fsp)) {
1125                 if (fsp->fh->fd != -1) {
1126                         *ret_fsp = fsp;
1127                         return NT_STATUS_OK;
1128                 }
1129         }
1130
1131         smb_fname_cp = cp_smb_filename(talloc_tos(),
1132                                         smb_fname);
1133         if (smb_fname_cp == NULL) {
1134                 return NT_STATUS_NO_MEMORY;
1135         }
1136
1137         /* Opens an INTERNAL_OPEN_ONLY write handle. */
1138         status = SMB_VFS_CREATE_FILE(
1139                 conn,                                   /* conn */
1140                 NULL,                                   /* req */
1141                 0,                                      /* root_dir_fid */
1142                 smb_fname_cp,                           /* fname */
1143                 FILE_WRITE_DATA,                        /* access_mask */
1144                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
1145                         FILE_SHARE_DELETE),
1146                 FILE_OPEN,                              /* create_disposition*/
1147                 0,                                      /* create_options */
1148                 0,                                      /* file_attributes */
1149                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1150                 NULL,                                   /* lease */
1151                 0,                                      /* allocation_size */
1152                 0,                                      /* private_flags */
1153                 NULL,                                   /* sd */
1154                 NULL,                                   /* ea_list */
1155                 ret_fsp,                                /* result */
1156                 NULL,                                   /* pinfo */
1157                 NULL, NULL);                            /* create context */
1158
1159         TALLOC_FREE(smb_fname_cp);
1160
1161         if (NT_STATUS_IS_OK(status)) {
1162                 *need_close = true;
1163         }
1164         return status;
1165 }