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