Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smb2_posix.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   SMB2 POSIX code.
4
   Copyright (C) Jeremy Allison                 2022
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 3 of the License, or
9
   (at your option) any later version.
10
11
   This program is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
#include "includes.h"
21
#include "smbd/smbd.h"
22
#include "passdb/lookup_sid.h"
23
#include "librpc/gen_ndr/ndr_security.h"
24
#include "librpc/gen_ndr/smb3posix.h"
25
#include "libcli/security/security.h"
26
#include "source3/modules/util_reparse.h"
27
#include "libcli/smb/reparse.h"
28
29
static NTSTATUS reparse_buffer_parse_posix_type(uint32_t reparse_tag,
30
            uint8_t *data,
31
            uint32_t len,
32
            mode_t *type)
33
0
{
34
0
  struct reparse_data_buffer *reparse = NULL;
35
0
  NTSTATUS status;
36
37
0
  if (reparse_tag == IO_REPARSE_TAG_SYMLINK) {
38
0
    *type = S_IFLNK;
39
0
    return NT_STATUS_OK;
40
0
  }
41
0
  if (reparse_tag != IO_REPARSE_TAG_NFS) {
42
    /*
43
     * Clients can create reparse points with arbitrary tags, return
44
     * anything that is not a NFS one (or symlink) as S_IFREG.
45
     */
46
0
    DBG_INFO("Unhandled NFS reparse tag: 0x%" PRIx32 "\n",
47
0
       reparse_tag);
48
0
    *type = S_IFREG;
49
0
    return NT_STATUS_OK;
50
0
  }
51
52
0
  reparse = talloc_zero(talloc_tos(), struct reparse_data_buffer);
53
0
  if (reparse == NULL) {
54
0
    DBG_ERR("talloc_zero() failed\n");
55
0
    return NT_STATUS_NO_MEMORY;
56
0
  }
57
58
0
  status = reparse_data_buffer_parse(reparse, reparse, data, len);
59
0
  if (!NT_STATUS_IS_OK(status)) {
60
0
    TALLOC_FREE(reparse);
61
0
    return status;
62
0
  }
63
64
0
  switch (reparse->parsed.nfs.type) {
65
0
  case NFS_SPECFILE_CHR:
66
0
    *type = S_IFCHR;
67
0
    break;
68
0
  case NFS_SPECFILE_BLK:
69
0
    *type = S_IFBLK;
70
0
    break;
71
0
  case NFS_SPECFILE_FIFO:
72
0
    *type = S_IFIFO;
73
0
    break;
74
0
  case NFS_SPECFILE_SOCK:
75
0
    *type = S_IFSOCK;
76
0
    break;
77
0
  default:
78
0
    DBG_ERR("Unhandled NFS reparse type: 0x%" PRIx64 "\n",
79
0
      reparse->parsed.nfs.type);
80
0
    TALLOC_FREE(reparse);
81
0
    return NT_STATUS_REPARSE_POINT_NOT_RESOLVED;
82
0
  }
83
84
0
  TALLOC_FREE(reparse);
85
0
  return status;
86
0
}
87
88
NTSTATUS smb3_file_posix_information_init(
89
  connection_struct *conn,
90
  const struct smb_filename *smb_fname,
91
  uint32_t dos_attributes,
92
  struct smb3_file_posix_information *dst)
93
0
{
94
0
  const struct stat_ex *st = &smb_fname->st;
95
0
  mode_t mode = st->st_ex_mode;
96
0
  uint32_t reparse_tag = 0;
97
0
  NTSTATUS status;
98
99
0
  switch (mode & S_IFMT) {
100
0
  case S_IFREG:
101
0
  case S_IFDIR:
102
0
    break;
103
0
  default:
104
    /*
105
     * All non-directory or regular files are reported
106
     * as reparse points. Client may or may not be able
107
     * to access these. This should already be set by
108
     * fdos_mode(), assert this.
109
     */
110
0
    SMB_ASSERT(dos_attributes & FILE_ATTRIBUTE_REPARSE_POINT);
111
0
    break;
112
0
  }
113
114
0
  if (dos_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
115
0
    uint8_t *reparse_data = NULL;
116
0
    uint32_t reparse_len;
117
0
    mode_t type = S_IFREG;
118
119
0
    status = fsctl_get_reparse_point(smb_fname->fsp,
120
0
             talloc_tos(),
121
0
             &reparse_tag,
122
0
             &reparse_data,
123
0
             UINT32_MAX,
124
0
             &reparse_len);
125
0
    if (!NT_STATUS_IS_OK(status)) {
126
0
      DBG_DEBUG("Could not get reparse point for %s: %s\n",
127
0
          smb_fname_str_dbg(smb_fname),
128
0
          nt_errstr(status));
129
0
      return status;
130
0
    }
131
132
0
    status = reparse_buffer_parse_posix_type(reparse_tag,
133
0
               reparse_data,
134
0
               reparse_len,
135
0
               &type);
136
0
    TALLOC_FREE(reparse_data);
137
0
    if (!NT_STATUS_IS_OK(status)) {
138
0
      DBG_DEBUG("Could not parse reparse data for %s: %s\n",
139
0
          smb_fname_str_dbg(smb_fname),
140
0
          nt_errstr(status));
141
0
      return status;
142
0
    }
143
144
    /*
145
     * Remove the type info we got via stat() and use what
146
     * we got from the reparse point.
147
     */
148
0
    mode &= ~S_IFMT;
149
0
    mode |= type;
150
0
  }
151
152
0
  *dst = (struct smb3_file_posix_information) {
153
0
    .end_of_file = get_file_size_stat(st),
154
0
    .allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn,NULL,st),
155
0
    .inode = SMB_VFS_FS_FILE_ID(conn, st),
156
0
    .device = st->st_ex_dev,
157
0
    .creation_time = unix_timespec_to_nt_time(st->st_ex_btime),
158
0
    .last_access_time = unix_timespec_to_nt_time(st->st_ex_atime),
159
0
    .last_write_time = unix_timespec_to_nt_time(st->st_ex_mtime),
160
0
    .change_time = unix_timespec_to_nt_time(st->st_ex_ctime),
161
0
    .file_attributes = dos_attributes,
162
0
    .cc.nlinks = st->st_ex_nlink,
163
0
    .cc.reparse_tag = reparse_tag,
164
0
    .cc.posix_mode = unix_mode_to_wire(mode),
165
0
    .cc.owner = global_sid_NULL,
166
0
    .cc.group = global_sid_NULL,
167
0
  };
168
169
0
  if (st->st_ex_uid != (uid_t)-1) {
170
0
    uid_to_sid(&dst->cc.owner, st->st_ex_uid);
171
0
  }
172
0
  if (st->st_ex_gid != (uid_t)-1) {
173
0
    gid_to_sid(&dst->cc.group, st->st_ex_gid);
174
0
  }
175
0
  return NT_STATUS_OK;
176
0
}