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