LCOV - code coverage report
Current view: top level - source3/smbd - dosmode.c (source / functions) Hit Total Coverage
Test: coverage report for master 6248eab5 Lines: 307 530 57.9 %
Date: 2021-08-25 13:27:56 Functions: 23 26 88.5 %

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

Generated by: LCOV version 1.13