LCOV - code coverage report
Current view: top level - source3/smbd - dosmode.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 325 527 61.7 %
Date: 2024-04-21 15:09:00 Functions: 22 23 95.7 %

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

Generated by: LCOV version 1.14