Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smbXsrv_version.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   Copyright (C) Stefan Metzmacher 2012
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 "system/filesys.h"
22
#include "smbd/globals.h"
23
#include "dbwrap/dbwrap.h"
24
#include "dbwrap/dbwrap_open.h"
25
#include "lib/util/util_tdb.h"
26
#include "librpc/gen_ndr/ndr_smbXsrv.h"
27
#include "serverid.h"
28
29
/*
30
 * This implements a version scheme for file server internal
31
 * states. smbXsrv_version_global.tdb stores the possible
32
 * and current versions of structure formats (struct smbXsrv_*_global)
33
 * per cluster node.
34
 *
35
 * If the supported versions doesn't match a version of any
36
 * of the other nodes, it refused to start.
37
 *
38
 * This should prevent silent corruption of the internal
39
 * databases and structures, if two incompatible implementations
40
 * read and write.
41
 *
42
 * In future this can be used to implement rolling code upgrades
43
 * in a cluster, but for now it is simple.
44
 */
45
46
static struct db_context *smbXsrv_version_global_db_ctx = NULL;
47
static uint32_t smbXsrv_version_global_current_version = UINT32_MAX;
48
49
NTSTATUS smbXsrv_version_global_init(const struct server_id *server_id)
50
0
{
51
0
  const char *global_path = NULL;
52
0
  struct db_context *db_ctx = NULL;
53
0
  struct db_record *db_rec = NULL;
54
0
  TDB_DATA key;
55
0
  TDB_DATA val;
56
0
  DATA_BLOB blob;
57
0
  struct smbXsrv_version_globalB global_blob;
58
0
  enum ndr_err_code ndr_err;
59
0
  struct smbXsrv_version_global0 *global = NULL;
60
0
  uint32_t i;
61
0
  uint32_t num_valid = 0;
62
0
  struct smbXsrv_version_node0 *valid = NULL;
63
0
  struct smbXsrv_version_node0 *local_node = NULL;
64
0
  bool exists;
65
0
  NTSTATUS status;
66
0
  const char *key_string = "smbXsrv_version_global";
67
0
  TALLOC_CTX *frame;
68
69
0
  if (smbXsrv_version_global_db_ctx != NULL) {
70
0
    return NT_STATUS_OK;
71
0
  }
72
73
0
  frame = talloc_stackframe();
74
75
0
  global_path = lock_path(talloc_tos(), "smbXsrv_version_global.tdb");
76
0
  if (global_path == NULL) {
77
0
    TALLOC_FREE(frame);
78
0
    return NT_STATUS_NO_MEMORY;
79
0
  }
80
81
0
  db_ctx = db_open(NULL, global_path,
82
0
       0, /* hash_size */
83
0
       TDB_DEFAULT |
84
0
       TDB_CLEAR_IF_FIRST |
85
0
       TDB_INCOMPATIBLE_HASH,
86
0
       O_RDWR | O_CREAT, 0600,
87
0
       DBWRAP_LOCK_ORDER_1,
88
0
       DBWRAP_FLAG_NONE);
89
0
  if (db_ctx == NULL) {
90
0
    status = map_nt_error_from_unix_common(errno);
91
0
    DBG_ERR("failed to open[%s] - %s\n",
92
0
      global_path,
93
0
      nt_errstr(status));
94
0
    TALLOC_FREE(frame);
95
0
    return status;
96
0
  }
97
98
0
  key = string_term_tdb_data(key_string);
99
100
0
  db_rec = dbwrap_fetch_locked(db_ctx, db_ctx, key);
101
0
  if (db_rec == NULL) {
102
0
    status = NT_STATUS_INTERNAL_DB_ERROR;
103
0
    DBG_ERR("dbwrap_fetch_locked(%s) - %s\n",
104
0
      key_string,
105
0
      nt_errstr(status));
106
107
0
    TALLOC_FREE(frame);
108
0
    return status;
109
0
  }
110
111
0
  val = dbwrap_record_get_value(db_rec);
112
0
  if (val.dsize == 0) {
113
0
    global = talloc_zero(frame, struct smbXsrv_version_global0);
114
0
    if (global == NULL) {
115
0
      DBG_ERR("talloc_zero failed - %s\n", __location__);
116
0
      TALLOC_FREE(frame);
117
0
      return NT_STATUS_NO_MEMORY;
118
0
    }
119
0
    global_blob = (struct smbXsrv_version_globalB) {
120
0
      .version = SMBXSRV_VERSION_CURRENT,
121
0
      .info.info0 = global,
122
0
    };
123
0
  } else {
124
0
    blob = data_blob_const(val.dptr, val.dsize);
125
126
0
    ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
127
0
      (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_version_globalB);
128
0
    if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
129
0
      status = ndr_map_error2ntstatus(ndr_err);
130
0
      DBG_ERR("ndr_pull_smbXsrv_version_globalB - %s\n",
131
0
        nt_errstr(status));
132
0
      TALLOC_FREE(frame);
133
0
      return status;
134
0
    }
135
136
0
    switch (global_blob.version) {
137
0
    case SMBXSRV_VERSION_0:
138
0
      global = global_blob.info.info0;
139
0
      if (global == NULL) {
140
0
        status = NT_STATUS_INTERNAL_DB_CORRUPTION;
141
0
        break;
142
0
      }
143
0
      status = NT_STATUS_OK;
144
0
      break;
145
0
    default:
146
0
      status = NT_STATUS_REVISION_MISMATCH;
147
0
      break;
148
0
    }
149
150
0
    if (!NT_STATUS_IS_OK(status)) {
151
0
      DBG_ERR("%s\n", nt_errstr(status));
152
0
      NDR_PRINT_DEBUG(smbXsrv_version_globalB, &global_blob);
153
0
      TALLOC_FREE(frame);
154
0
      return status;
155
0
    }
156
0
  }
157
158
0
  valid = talloc_zero_array(global,
159
0
          struct smbXsrv_version_node0,
160
0
          global->num_nodes + 1);
161
0
  if (valid == NULL) {
162
0
    DBG_ERR("talloc_zero_array failed - %s\n", __location__);
163
0
    TALLOC_FREE(frame);
164
0
    return NT_STATUS_NO_MEMORY;
165
0
  }
166
167
0
  num_valid = 0;
168
0
  for (i=0; i < global->num_nodes; i++) {
169
0
    struct smbXsrv_version_node0 *n = &global->nodes[i];
170
171
0
    exists = serverid_exists(&n->server_id);
172
0
    if (!exists) {
173
0
      continue;
174
0
    }
175
176
0
    if (n->min_version > n->max_version) {
177
0
      status = NT_STATUS_INTERNAL_DB_CORRUPTION;
178
0
      DBG_ERR("%s\n", nt_errstr(status));
179
0
      NDR_PRINT_DEBUG(smbXsrv_version_globalB, &global_blob);
180
0
      TALLOC_FREE(frame);
181
0
      return status;
182
0
    }
183
184
0
    if (n->min_version > global_blob.version) {
185
0
      status = NT_STATUS_INTERNAL_DB_CORRUPTION;
186
0
      DBG_ERR("%s\n", nt_errstr(status));
187
0
      NDR_PRINT_DEBUG(smbXsrv_version_globalB, &global_blob);
188
0
      TALLOC_FREE(frame);
189
0
      return status;
190
0
    }
191
192
0
    if (n->max_version < global_blob.version) {
193
0
      status = NT_STATUS_INTERNAL_DB_CORRUPTION;
194
0
      DBG_ERR("%s\n", nt_errstr(status));
195
0
      NDR_PRINT_DEBUG(smbXsrv_version_globalB, &global_blob);
196
0
      TALLOC_FREE(frame);
197
0
      return status;
198
0
    }
199
200
0
    valid[num_valid] = *n;
201
0
    if (server_id->vnn == n->server_id.vnn) {
202
0
      local_node = &valid[num_valid];
203
0
    }
204
0
    num_valid++;
205
0
  }
206
207
0
  if (local_node == NULL) {
208
0
    local_node = &valid[num_valid];
209
0
    num_valid++;
210
0
  }
211
212
0
  *local_node = (struct smbXsrv_version_node0){
213
0
    .server_id = *server_id,
214
0
    .min_version = SMBXSRV_VERSION_0,
215
0
    .max_version = SMBXSRV_VERSION_CURRENT,
216
0
    .current_version = global_blob.version,
217
0
  };
218
219
0
  global->num_nodes = num_valid;
220
0
  global->nodes = valid;
221
222
0
  global_blob.seqnum += 1;
223
0
  global_blob.info.info0 = global;
224
225
0
  ndr_err = ndr_push_struct_blob(&blob, db_rec, &global_blob,
226
0
      (ndr_push_flags_fn_t)ndr_push_smbXsrv_version_globalB);
227
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
228
0
    status = ndr_map_error2ntstatus(ndr_err);
229
0
    DBG_ERR("ndr_push_smbXsrv_version_globalB - %s\n",
230
0
      nt_errstr(status));
231
0
    TALLOC_FREE(frame);
232
0
    return status;
233
0
  }
234
235
0
  val = make_tdb_data(blob.data, blob.length);
236
0
  status = dbwrap_record_store(db_rec, val, TDB_REPLACE);
237
0
  TALLOC_FREE(db_rec);
238
0
  if (!NT_STATUS_IS_OK(status)) {
239
0
    DBG_ERR("dbwrap_record_store - %s\n", nt_errstr(status));
240
0
    TALLOC_FREE(frame);
241
0
    return status;
242
0
  }
243
244
0
  DBG_DEBUG("\n");
245
0
  if (DEBUGLVL(10)) {
246
0
    NDR_PRINT_DEBUG(smbXsrv_version_globalB, &global_blob);
247
0
  }
248
249
0
  smbXsrv_version_global_db_ctx = db_ctx;
250
0
  smbXsrv_version_global_current_version = global_blob.version;
251
252
0
  TALLOC_FREE(frame);
253
0
  return NT_STATUS_OK;
254
0
}
255
256
uint32_t smbXsrv_version_global_current(void)
257
0
{
258
0
  return smbXsrv_version_global_current_version;
259
0
}