LCOV - code coverage report
Current view: top level - source3/smbd - dosmode.c (source / functions) Hit Total Coverage
Test: coverage report for recycleplus df22b230 Lines: 218 485 44.9 %
Date: 2024-02-14 10:14:15 Functions: 18 23 78.3 %

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

Generated by: LCOV version 1.14