Correct "succeded" typos.
[nivanova/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         /*
388          * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
389          * vfs_default via DMAPI if that is enabled.
390          */
391         dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
392
393         ZERO_STRUCT(dosattrib);
394         ZERO_STRUCT(blob);
395
396         dosattrib.version = 3;
397         dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
398                                         XATTR_DOSINFO_CREATE_TIME;
399         dosattrib.info.info3.attrib = dosmode;
400         dosattrib.info.info3.create_time = unix_timespec_to_nt_time(
401                                 smb_fname->st.st_ex_btime);
402
403         DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
404                 (unsigned int)dosmode,
405                 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
406                 smb_fname_str_dbg(smb_fname) ));
407
408         ndr_err = ndr_push_struct_blob(
409                         &blob, talloc_tos(), &dosattrib,
410                         (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
411
412         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
413                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
414                         ndr_errstr(ndr_err)));
415                 return ndr_map_error2ntstatus(ndr_err);
416         }
417
418         if (blob.data == NULL || blob.length == 0) {
419                 /* Should this be INTERNAL_ERROR? */
420                 return NT_STATUS_INVALID_PARAMETER;
421         }
422
423         if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
424                              SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
425                              0) == -1) {
426                 NTSTATUS status = NT_STATUS_OK;
427                 bool need_close = false;
428                 files_struct *fsp = NULL;
429
430                 if((errno != EPERM) && (errno != EACCES)) {
431                         DBG_INFO("Cannot set "
432                                  "attribute EA on file %s: Error = %s\n",
433                                  smb_fname_str_dbg(smb_fname), strerror(errno));
434                         return map_nt_error_from_unix(errno);
435                 }
436
437                 /* We want DOS semantics, ie allow non owner with write permission to change the
438                         bits on a file. Just like file_ntimes below.
439                 */
440
441                 /* Check if we have write access. */
442                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
443                         return NT_STATUS_ACCESS_DENIED;
444
445                 if (!can_write_to_file(conn, smb_fname)) {
446                         return NT_STATUS_ACCESS_DENIED;
447                 }
448
449                 /*
450                  * We need to get an open file handle to do the
451                  * metadata operation under root.
452                  */
453
454                 status = get_file_handle_for_metadata(conn,
455                                                 smb_fname,
456                                                 &fsp,
457                                                 &need_close);
458                 if (!NT_STATUS_IS_OK(status)) {
459                         return status;
460                 }
461
462                 become_root();
463                 if (SMB_VFS_FSETXATTR(fsp,
464                                      SAMBA_XATTR_DOS_ATTRIB, blob.data,
465                                      blob.length, 0) == 0) {
466                         status = NT_STATUS_OK;
467                 }
468                 unbecome_root();
469                 if (need_close) {
470                         close_file(NULL, fsp, NORMAL_CLOSE);
471                 }
472                 return status;
473         }
474         DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
475                 (unsigned int)dosmode,
476                 smb_fname_str_dbg(smb_fname)));
477         return NT_STATUS_OK;
478 }
479
480 /****************************************************************************
481  Change a unix mode to a dos mode for an ms dfs link.
482 ****************************************************************************/
483
484 uint32_t dos_mode_msdfs(connection_struct *conn,
485                       const struct smb_filename *smb_fname)
486 {
487         uint32_t result = 0;
488
489         DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
490
491         if (!VALID_STAT(smb_fname->st)) {
492                 return 0;
493         }
494
495         /* First do any modifications that depend on the path name. */
496         /* hide files with a name starting with a . */
497         if (lp_hide_dot_files(SNUM(conn))) {
498                 const char *p = strrchr_m(smb_fname->base_name, '/');
499                 if (p) {
500                         p++;
501                 } else {
502                         p = smb_fname->base_name;
503                 }
504
505                 /* Only . and .. are not hidden. */
506                 if (p[0] == '.' && !((p[1] == '\0') ||
507                                 (p[1] == '.' && p[2] == '\0'))) {
508                         result |= FILE_ATTRIBUTE_HIDDEN;
509                 }
510         }
511
512         result |= dos_mode_from_sbuf(conn, smb_fname);
513
514         /* Optimization : Only call is_hidden_path if it's not already
515            hidden. */
516         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
517             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
518                 result |= FILE_ATTRIBUTE_HIDDEN;
519         }
520
521         if (result == 0) {
522                 result = FILE_ATTRIBUTE_NORMAL;
523         }
524
525         result = filter_mode_by_protocol(result);
526
527         /*
528          * Add in that it is a reparse point
529          */
530         result |= FILE_ATTRIBUTE_REPARSE_POINT;
531
532         dos_mode_debug_print(__func__, result);
533
534         return(result);
535 }
536
537 /*
538  * check whether a file or directory is flagged as compressed.
539  */
540 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
541                                           struct smb_filename *smb_fname,
542                                           bool *is_compressed)
543 {
544         NTSTATUS status;
545         uint16_t compression_fmt;
546         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
547         if (tmp_ctx == NULL) {
548                 status = NT_STATUS_NO_MEMORY;
549                 goto err_out;
550         }
551
552         status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
553                                          &compression_fmt);
554         if (!NT_STATUS_IS_OK(status)) {
555                 goto err_ctx_free;
556         }
557
558         if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
559                 *is_compressed = true;
560         } else {
561                 *is_compressed = false;
562         }
563         status = NT_STATUS_OK;
564
565 err_ctx_free:
566         talloc_free(tmp_ctx);
567 err_out:
568         return status;
569 }
570
571 static uint32_t dos_mode_from_name(connection_struct *conn,
572                                    const struct smb_filename *smb_fname,
573                                    uint32_t dosmode)
574 {
575         const char *p = NULL;
576         uint32_t result = dosmode;
577
578         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
579             lp_hide_dot_files(SNUM(conn)))
580         {
581                 p = strrchr_m(smb_fname->base_name, '/');
582                 if (p) {
583                         p++;
584                 } else {
585                         p = smb_fname->base_name;
586                 }
587
588                 /* Only . and .. are not hidden. */
589                 if ((p[0] == '.') &&
590                     !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
591                 {
592                         result |= FILE_ATTRIBUTE_HIDDEN;
593                 }
594         }
595
596         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
597             IS_HIDDEN_PATH(conn, smb_fname->base_name))
598         {
599                 result |= FILE_ATTRIBUTE_HIDDEN;
600         }
601
602         return result;
603 }
604
605 /****************************************************************************
606  Change a unix mode to a dos mode.
607  May also read the create timespec into the stat struct in smb_fname
608  if "store dos attributes" is true.
609 ****************************************************************************/
610
611 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
612 {
613         uint32_t result = 0;
614         NTSTATUS status = NT_STATUS_OK;
615
616         DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
617
618         if (!VALID_STAT(smb_fname->st)) {
619                 return 0;
620         }
621
622         /* Get the DOS attributes via the VFS if we can */
623         status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
624         if (!NT_STATUS_IS_OK(status)) {
625                 result |= dos_mode_from_sbuf(conn, smb_fname);
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 (S_ISDIR(smb_fname->st.st_ex_mode)) {
640                 result |= FILE_ATTRIBUTE_DIRECTORY;
641         } else if (result == 0) {
642                 result = FILE_ATTRIBUTE_NORMAL;
643         }
644
645         result = filter_mode_by_protocol(result);
646
647         dos_mode_debug_print(__func__, result);
648
649         return result;
650 }
651
652 /*******************************************************************
653  chmod a file - but preserve some bits.
654  If "store dos attributes" is also set it will store the create time
655  from the stat struct in smb_fname (in NTTIME format) in the EA
656  attribute also.
657 ********************************************************************/
658
659 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
660                      uint32_t dosmode, const char *parent_dir, bool newfile)
661 {
662         int mask=0;
663         mode_t tmp;
664         mode_t unixmode;
665         int ret = -1, lret = -1;
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         dosmode &= SAMBA_ATTRIBUTES_MASK;
676
677         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
678                   dosmode, smb_fname_str_dbg(smb_fname)));
679
680         unixmode = smb_fname->st.st_ex_mode;
681
682         get_acl_group_bits(conn, smb_fname->base_name,
683                            &smb_fname->st.st_ex_mode);
684
685         if (S_ISDIR(smb_fname->st.st_ex_mode))
686                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
687         else
688                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
689
690         /* Store the DOS attributes in an EA by preference. */
691         status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
692         if (NT_STATUS_IS_OK(status)) {
693                 if (!newfile) {
694                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
695                                 FILE_NOTIFY_CHANGE_ATTRIBUTES,
696                                 smb_fname->base_name);
697                 }
698                 smb_fname->st.st_ex_mode = unixmode;
699                 return 0;
700         } else {
701                 /*
702                  * Only fall back to using UNIX modes if
703                  * we get NOT_IMPLEMENTED.
704                  */
705                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
706                         errno = map_errno_from_nt_status(status);
707                         return -1;
708                 }
709         }
710
711         /* Fall back to UNIX modes. */
712         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
713
714         /* preserve the file type bits */
715         mask |= S_IFMT;
716
717         /* preserve the s bits */
718         mask |= (S_ISUID | S_ISGID);
719
720         /* preserve the t bit */
721 #ifdef S_ISVTX
722         mask |= S_ISVTX;
723 #endif
724
725         /* possibly preserve the x bits */
726         if (!MAP_ARCHIVE(conn))
727                 mask |= S_IXUSR;
728         if (!MAP_SYSTEM(conn))
729                 mask |= S_IXGRP;
730         if (!MAP_HIDDEN(conn))
731                 mask |= S_IXOTH;
732
733         unixmode |= (smb_fname->st.st_ex_mode & mask);
734
735         /* if we previously had any r bits set then leave them alone */
736         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
737                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
738                 unixmode |= tmp;
739         }
740
741         /* if we previously had any w bits set then leave them alone 
742                 whilst adding in the new w bits, if the new mode is not rdonly */
743         if (!IS_DOS_READONLY(dosmode)) {
744                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
745         }
746
747         /*
748          * From the chmod 2 man page:
749          *
750          * "If the calling process is not privileged, and the group of the file
751          * does not match the effective group ID of the process or one of its
752          * supplementary group IDs, the S_ISGID bit will be turned off, but
753          * this will not cause an error to be returned."
754          *
755          * Simply refuse to do the chmod in this case.
756          */
757
758         if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
759                         geteuid() != sec_initial_uid() &&
760                         !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
761                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
762                         "set for directory %s\n",
763                         smb_fname_str_dbg(smb_fname)));
764                 errno = EPERM;
765                 return -1;
766         }
767
768         ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
769         if (ret == 0) {
770                 if(!newfile || (lret != -1)) {
771                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
772                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
773                                      smb_fname->base_name);
774                 }
775                 smb_fname->st.st_ex_mode = unixmode;
776                 return 0;
777         }
778
779         if((errno != EPERM) && (errno != EACCES))
780                 return -1;
781
782         if(!lp_dos_filemode(SNUM(conn)))
783                 return -1;
784
785         /* We want DOS semantics, ie allow non owner with write permission to change the
786                 bits on a file. Just like file_ntimes below.
787         */
788
789         if (!can_write_to_file(conn, smb_fname)) {
790                 errno = EACCES;
791                 return -1;
792         }
793
794         /*
795          * We need to get an open file handle to do the
796          * metadata operation under root.
797          */
798
799         status = get_file_handle_for_metadata(conn,
800                                               smb_fname,
801                                               &fsp,
802                                               &need_close);
803         if (!NT_STATUS_IS_OK(status)) {
804                 errno = map_errno_from_nt_status(status);
805                 return -1;
806         }
807
808         become_root();
809         ret = SMB_VFS_FCHMOD(fsp, unixmode);
810         unbecome_root();
811         if (need_close) {
812                 close_file(NULL, fsp, NORMAL_CLOSE);
813         }
814         if (!newfile) {
815                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
816                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
817                              smb_fname->base_name);
818         }
819         if (ret == 0) {
820                 smb_fname->st.st_ex_mode = unixmode;
821         }
822
823         return( ret );
824 }
825
826
827 NTSTATUS file_set_sparse(connection_struct *conn,
828                          files_struct *fsp,
829                          bool sparse)
830 {
831         uint32_t old_dosmode;
832         uint32_t new_dosmode;
833         NTSTATUS status;
834
835         if (!CAN_WRITE(conn)) {
836                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
837                         "on readonly share[%s]\n",
838                         smb_fname_str_dbg(fsp->fsp_name),
839                         sparse,
840                         lp_servicename(talloc_tos(), SNUM(conn))));
841                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
842         }
843
844         /*
845          * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
846          * following access flags are granted.
847          */
848         if ((fsp->access_mask & (FILE_WRITE_DATA
849                                 | FILE_WRITE_ATTRIBUTES
850                                 | SEC_FILE_APPEND_DATA)) == 0) {
851                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
852                         "access_mask[0x%08X] - access denied\n",
853                         smb_fname_str_dbg(fsp->fsp_name),
854                         sparse,
855                         fsp->access_mask));
856                 return NT_STATUS_ACCESS_DENIED;
857         }
858
859         if (fsp->is_directory) {
860                 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
861                           (sparse ? "set" : "clear"),
862                           smb_fname_str_dbg(fsp->fsp_name)));
863                 return NT_STATUS_INVALID_PARAMETER;
864         }
865
866         if (IS_IPC(conn) || IS_PRINT(conn)) {
867                 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
868                           (sparse ? "set" : "clear")));
869                 return NT_STATUS_INVALID_PARAMETER;
870         }
871
872         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
873                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
874
875         if (!lp_store_dos_attributes(SNUM(conn))) {
876                 return NT_STATUS_INVALID_DEVICE_REQUEST;
877         }
878
879         status = vfs_stat_fsp(fsp);
880         if (!NT_STATUS_IS_OK(status)) {
881                 return status;
882         }
883
884         old_dosmode = dos_mode(conn, fsp->fsp_name);
885
886         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
887                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
888         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
889                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
890         } else {
891                 return NT_STATUS_OK;
892         }
893
894         /* Store the DOS attributes in an EA. */
895         status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
896         if (!NT_STATUS_IS_OK(status)) {
897                 return status;
898         }
899
900         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
901                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
902                      fsp->fsp_name->base_name);
903
904         fsp->is_sparse = sparse;
905
906         return NT_STATUS_OK;
907 }
908
909 /*******************************************************************
910  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
911  than POSIX.
912 *******************************************************************/
913
914 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
915                 struct smb_file_time *ft)
916 {
917         int ret = -1;
918
919         errno = 0;
920
921         DEBUG(6, ("file_ntime: actime: %s",
922                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
923         DEBUG(6, ("file_ntime: modtime: %s",
924                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
925         DEBUG(6, ("file_ntime: ctime: %s",
926                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
927         DEBUG(6, ("file_ntime: createtime: %s",
928                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
929
930         /* Don't update the time on read-only shares */
931         /* We need this as set_filetime (which can be called on
932            close and other paths) can end up calling this function
933            without the NEED_WRITE protection. Found by : 
934            Leo Weppelman <leo@wau.mis.ah.nl>
935         */
936
937         if (!CAN_WRITE(conn)) {
938                 return 0;
939         }
940
941         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
942                 return 0;
943         }
944
945         if((errno != EPERM) && (errno != EACCES)) {
946                 return -1;
947         }
948
949         if(!lp_dos_filetimes(SNUM(conn))) {
950                 return -1;
951         }
952
953         /* We have permission (given by the Samba admin) to
954            break POSIX semantics and allow a user to change
955            the time on a file they don't own but can write to
956            (as DOS does).
957          */
958
959         /* Check if we have write access. */
960         if (can_write_to_file(conn, smb_fname)) {
961                 /* We are allowed to become root and change the filetime. */
962                 become_root();
963                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
964                 unbecome_root();
965         }
966
967         return ret;
968 }
969
970 /******************************************************************
971  Force a "sticky" write time on a pathname. This will always be
972  returned on all future write time queries and set on close.
973 ******************************************************************/
974
975 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
976 {
977         if (null_timespec(mtime)) {
978                 return true;
979         }
980
981         if (!set_sticky_write_time(fileid, mtime)) {
982                 return false;
983         }
984
985         return true;
986 }
987
988 /******************************************************************
989  Force a "sticky" write time on an fsp. This will always be
990  returned on all future write time queries and set on close.
991 ******************************************************************/
992
993 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
994 {
995         if (null_timespec(mtime)) {
996                 return true;
997         }
998
999         fsp->write_time_forced = true;
1000         TALLOC_FREE(fsp->update_write_time_event);
1001
1002         return set_sticky_write_time_path(fsp->file_id, mtime);
1003 }
1004
1005 /******************************************************************
1006  Set a create time EA.
1007 ******************************************************************/
1008
1009 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1010                                 const struct smb_filename *psmb_fname,
1011                                 struct timespec create_time)
1012 {
1013         struct smb_filename *smb_fname;
1014         uint32_t dosmode;
1015         int ret;
1016
1017         if (!lp_store_dos_attributes(SNUM(conn))) {
1018                 return NT_STATUS_OK;
1019         }
1020
1021         smb_fname = synthetic_smb_fname(talloc_tos(),
1022                                         psmb_fname->base_name,
1023                                         NULL,
1024                                         &psmb_fname->st,
1025                                         psmb_fname->flags);
1026
1027         if (smb_fname == NULL) {
1028                 return NT_STATUS_NO_MEMORY;
1029         }
1030
1031         dosmode = dos_mode(conn, smb_fname);
1032
1033         smb_fname->st.st_ex_btime = create_time;
1034
1035         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1036         if (ret == -1) {
1037                 return map_nt_error_from_unix(errno);
1038         }
1039
1040         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1041                 smb_fname_str_dbg(smb_fname)));
1042
1043         return NT_STATUS_OK;
1044 }
1045
1046 /******************************************************************
1047  Return a create time.
1048 ******************************************************************/
1049
1050 struct timespec get_create_timespec(connection_struct *conn,
1051                                 struct files_struct *fsp,
1052                                 const struct smb_filename *smb_fname)
1053 {
1054         return smb_fname->st.st_ex_btime;
1055 }
1056
1057 /******************************************************************
1058  Return a change time (may look at EA in future).
1059 ******************************************************************/
1060
1061 struct timespec get_change_timespec(connection_struct *conn,
1062                                 struct files_struct *fsp,
1063                                 const struct smb_filename *smb_fname)
1064 {
1065         return smb_fname->st.st_ex_mtime;
1066 }
1067
1068 /****************************************************************************
1069  Get a real open file handle we can do meta-data operations on. As it's
1070  going to be used under root access only on meta-data we should look for
1071  any existing open file handle first, and use that in preference (also to
1072  avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1073 ****************************************************************************/
1074
1075 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1076                                 const struct smb_filename *smb_fname,
1077                                 files_struct **ret_fsp,
1078                                 bool *need_close)
1079 {
1080         NTSTATUS status;
1081         files_struct *fsp;
1082         struct file_id file_id;
1083         struct smb_filename *smb_fname_cp = NULL;
1084
1085         *need_close = false;
1086
1087         if (!VALID_STAT(smb_fname->st)) {
1088                 return NT_STATUS_INVALID_PARAMETER;
1089         }
1090
1091         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1092
1093         for(fsp = file_find_di_first(conn->sconn, file_id);
1094                         fsp;
1095                         fsp = file_find_di_next(fsp)) {
1096                 if (fsp->fh->fd != -1) {
1097                         *ret_fsp = fsp;
1098                         return NT_STATUS_OK;
1099                 }
1100         }
1101
1102         smb_fname_cp = cp_smb_filename(talloc_tos(),
1103                                         smb_fname);
1104         if (smb_fname_cp == NULL) {
1105                 return NT_STATUS_NO_MEMORY;
1106         }
1107
1108         /* Opens an INTERNAL_OPEN_ONLY write handle. */
1109         status = SMB_VFS_CREATE_FILE(
1110                 conn,                                   /* conn */
1111                 NULL,                                   /* req */
1112                 0,                                      /* root_dir_fid */
1113                 smb_fname_cp,                           /* fname */
1114                 FILE_WRITE_DATA,                        /* access_mask */
1115                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
1116                         FILE_SHARE_DELETE),
1117                 FILE_OPEN,                              /* create_disposition*/
1118                 0,                                      /* create_options */
1119                 0,                                      /* file_attributes */
1120                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1121                 NULL,                                   /* lease */
1122                 0,                                      /* allocation_size */
1123                 0,                                      /* private_flags */
1124                 NULL,                                   /* sd */
1125                 NULL,                                   /* ea_list */
1126                 ret_fsp,                                /* result */
1127                 NULL,                                   /* pinfo */
1128                 NULL, NULL);                            /* create context */
1129
1130         TALLOC_FREE(smb_fname_cp);
1131
1132         if (NT_STATUS_IS_OK(status)) {
1133                 *need_close = true;
1134         }
1135         return status;
1136 }