don't use 'open'
[metze/samba/wip.git] / source3 / smbd / smbXsrv_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Stefan Metzmacher 2012
5    Copyright (C) Michael Adam 2012
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 "smbd/smbd.h"
23 #include "smbd/globals.h"
24 #include "dbwrap/dbwrap.h"
25 #include "dbwrap/dbwrap_rbt.h"
26 #include "dbwrap/dbwrap_open.h"
27 #include "session.h"
28 #include "auth.h"
29 #include "../lib/tsocket/tsocket.h"
30 #include "../libcli/security/security.h"
31 #include "messages.h"
32 #include "lib/util/util_tdb.h"
33 #include "librpc/gen_ndr/ndr_smbXsrv.h"
34
35 static struct db_context *smbXsrv_open_global_db_ctx = NULL;
36
37 NTSTATUS smbXsrv_open_global_init(void)
38 {
39         const char *global_path = NULL;
40         struct db_context *db_ctx = NULL;
41
42         if (smbXsrv_open_global_db_ctx != NULL) {
43                 return NT_STATUS_OK;
44         }
45
46         global_path = lock_path("smbXsrv_open_global.tdb");
47
48         db_ctx = db_open(NULL, global_path,
49                          0, /* hash_size */
50                          TDB_DEFAULT |
51                          TDB_CLEAR_IF_FIRST |
52                          TDB_INCOMPATIBLE_HASH,
53                          O_RDWR | O_CREAT, 0600,
54                          DBWRAP_LOCK_ORDER_1 /* TODO - correct lock order */);
55         if (db_ctx == NULL) {
56                 NTSTATUS status;
57
58                 status = map_nt_error_from_unix_common(errno);
59
60                 return status;
61         }
62
63         smbXsrv_open_global_db_ctx = db_ctx;
64
65         return NT_STATUS_OK;
66 }
67
68 static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn,
69                                            uint32_t lowest_id,
70                                            uint32_t highest_id)
71 {
72         struct smbXsrv_open_table *table = &conn->open_table;
73         NTSTATUS status;
74
75         ZERO_STRUCTP(table);
76         table->local.db_ctx = db_open_rbt(conn);
77         if (table->local.db_ctx == NULL) {
78                 return NT_STATUS_NO_MEMORY;
79         }
80         table->local.lowest_id = lowest_id;
81         table->local.highest_id = highest_id;
82
83         status = smbXsrv_open_global_init();
84         if (!NT_STATUS_IS_OK(status)) {
85                 return status;
86         }
87
88         table->global.db_ctx = smbXsrv_open_global_db_ctx;
89
90         return NT_STATUS_OK;
91 }
92
93 struct smbXsrv_open_local_allocate_state {
94         const uint32_t lowest_id;
95         const uint32_t highest_id;
96         uint32_t last_id;
97         uint32_t useable_id;
98         NTSTATUS status;
99 };
100
101 static int smbXsrv_open_local_allocate_traverse(struct db_record *rec,
102                                                    void *private_data)
103 {
104         struct smbXsrv_open_local_allocate_state *state =
105                 (struct smbXsrv_open_local_allocate_state *)private_data;
106         TDB_DATA key = dbwrap_record_get_key(rec);
107         uint32_t id = 0;
108
109         if (key.dsize != sizeof(uint32_t)) {
110                 //TODO errno?
111                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
112                 return -1;
113         }
114
115         /*
116          * NOTE:
117          * We need big endian so that dbwrap_rbt's memcmp
118          * has the same result as integer comparison between the uint32_t
119          * values.
120          *
121          * TODO: implement string based key
122          */
123         id = RIVAL(key.dptr, 0);
124
125         if (id <= state->last_id) {
126                 //TODO errno?
127                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
128                 return -1;
129         }
130         state->last_id = id;
131
132         if (id > state->useable_id) {
133                 state->status = NT_STATUS_OK;
134                 return -1;
135         }
136
137         state->useable_id +=1;
138         return 0;
139 }
140
141 static NTSTATUS smbXsrv_open_local_allocate_id(struct db_context *db,
142                                                uint32_t lowest_id,
143                                                uint32_t highest_id,
144                                                TALLOC_CTX *mem_ctx,
145                                                struct db_record **_rec,
146                                                uint32_t *_id)
147 {
148         struct smbXsrv_open_local_allocate_state state = {
149                 .lowest_id = lowest_id,
150                 .highest_id = highest_id,
151                 .last_id = 0,
152                 .useable_id = lowest_id,
153                 .status = NT_STATUS_INTERNAL_ERROR,
154         };
155         uint32_t i;
156         uint32_t range;
157         NTSTATUS status;
158         int count = 0;
159
160         *_rec = NULL;
161         *_id = 0;
162
163         if (lowest_id > highest_id) {
164                 return NT_STATUS_INSUFFICIENT_RESOURCES;
165         }
166
167         range = (highest_id - lowest_id) + 1;
168
169         for (i = 0; i < range; i++) {
170                 uint32_t id;
171                 uint8_t key_buf[sizeof(uint32_t)];
172                 TDB_DATA key;
173                 TDB_DATA val;
174                 struct db_record *rec = NULL;
175
176                 id = generate_random() % range;
177                 id += lowest_id;
178
179                 if (id < lowest_id) {
180                         id = lowest_id;
181                 }
182                 if (id > highest_id) {
183                         id = highest_id;
184                 }
185
186                 RSIVAL(key_buf, 0, id);
187                 key = make_tdb_data(key_buf, sizeof(key_buf));
188
189                 rec = dbwrap_fetch_locked(db, mem_ctx, key);
190                 if (rec == NULL) {
191                         return NT_STATUS_INSUFFICIENT_RESOURCES;
192                 }
193
194                 val = dbwrap_record_get_value(rec);
195                 if (val.dsize != 0) {
196                         TALLOC_FREE(rec);
197                         continue;
198                 }
199
200                 *_rec = rec;
201                 *_id = id;
202                 return NT_STATUS_OK;
203         }
204
205         status = dbwrap_traverse_read(db, smbXsrv_open_local_allocate_traverse,
206                                       &state, &count);
207         if (!NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)) {
208                 /*
209                  * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
210                  *
211                  * If we get anything else it is an error, because it
212                  * means we did not manage to find a free slot in
213                  * the db.
214                  */
215                 return NT_STATUS_INSUFFICIENT_RESOURCES;
216         }
217
218         if (NT_STATUS_IS_OK(state.status)) {
219                 uint32_t id;
220                 uint8_t key_buf[sizeof(uint32_t)];
221                 TDB_DATA key;
222                 TDB_DATA val;
223                 struct db_record *rec = NULL;
224
225                 id = state.useable_id;
226
227                 RSIVAL(key_buf, 0, id);
228                 key = make_tdb_data(key_buf, sizeof(key_buf));
229
230                 rec = dbwrap_fetch_locked(db, mem_ctx, key);
231                 if (rec == NULL) {
232                         return NT_STATUS_INSUFFICIENT_RESOURCES;
233                 }
234
235                 val = dbwrap_record_get_value(rec);
236                 if (val.dsize != 0) {
237                         TALLOC_FREE(rec);
238                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
239                 }
240
241                 *_rec = rec;
242                 *_id = id;
243                 return NT_STATUS_OK;
244         }
245
246         return state.status;
247 }
248
249 struct smbXsrv_open_local_fetch_state {
250         struct smbXsrv_open *op;
251         NTSTATUS status;
252 };
253
254 static void smbXsrv_open_local_fetch_parser(TDB_DATA key, TDB_DATA data,
255                                                void *private_data)
256 {
257         struct smbXsrv_open_local_fetch_state *state =
258                 (struct smbXsrv_open_local_fetch_state *)private_data;
259         void *ptr;
260
261         if (data.dsize != sizeof(ptr)) {
262                 state->status = NT_STATUS_INTERNAL_DB_ERROR;
263                 return;
264         }
265
266         memcpy(&ptr, data.dptr, data.dsize);
267         state->op = talloc_get_type_abort(ptr, struct smbXsrv_open);
268         state->status = NT_STATUS_OK;
269 }
270
271 static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table,
272                                           uint32_t open_local_id,
273                                           NTTIME now,
274                                           struct smbXsrv_open **_open)
275 {
276         struct smbXsrv_open_local_fetch_state state = {
277                 .op = NULL,
278                 .status = NT_STATUS_INTERNAL_ERROR,
279         };
280         uint8_t key_buf[sizeof(uint32_t)];
281         TDB_DATA key;
282         NTSTATUS status;
283
284         *_open = NULL;
285
286         if (table->local.db_ctx == NULL) {
287                 return NT_STATUS_INTERNAL_ERROR;
288         }
289
290         RSIVAL(key_buf, 0, open_local_id);
291         key = make_tdb_data(key_buf, sizeof(key_buf));
292
293         status = dbwrap_parse_record(table->local.db_ctx, key,
294                                      smbXsrv_open_local_fetch_parser,
295                                      &state);
296         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
297                 return NT_STATUS_USER_SESSION_DELETED;
298         } else if (!NT_STATUS_IS_OK(status)) {
299                 return status;
300         }
301         if (!NT_STATUS_IS_OK(state.status)) {
302                 return state.status;
303         }
304
305         *_open = state.op;
306         return NT_STATUS_OK;
307 }
308
309 static int smbXsrv_open_global_destructor(struct smbXsrv_open_global0 *global)
310 {
311         return 0;
312 }
313
314 static NTSTATUS smbXsrv_open_global_allocate(struct db_context *db,
315                                         TALLOC_CTX *mem_ctx,
316                                         struct smbXsrv_open_global0 **_global)
317 {
318         uint32_t i;
319         struct smbXsrv_open_global0 *global = NULL;
320
321         *_global = NULL;
322
323         global = talloc_zero(mem_ctx, struct smbXsrv_open_global0);
324         if (global == NULL) {
325                 return NT_STATUS_NO_MEMORY;
326         }
327         talloc_set_destructor(global, smbXsrv_open_global_destructor);
328
329         for (i = 0; i < UINT32_MAX; i++) {
330                 uint32_t id;
331                 uint8_t key_buf[sizeof(uint32_t)];
332                 TDB_DATA key;
333                 TDB_DATA val;
334
335                 id = generate_random();
336                 if (id == 0) {
337                         id++;
338                 }
339                 if (id == UINT32_MAX) {
340                         id--;
341                 }
342
343                 RSIVAL(key_buf, 0, id);
344                 key = make_tdb_data(key_buf, sizeof(key_buf));
345
346                 global->db_rec = dbwrap_fetch_locked(db, mem_ctx, key);
347                 if (global->db_rec == NULL) {
348                         talloc_free(global);
349                         return NT_STATUS_INSUFFICIENT_RESOURCES;
350                 }
351
352                 val = dbwrap_record_get_value(global->db_rec);
353                 if (val.dsize != 0) {
354                         TALLOC_FREE(global->db_rec);
355                         continue;
356                 }
357
358                 global->open_global_id = id;
359
360                 *_global = global;
361                 return NT_STATUS_OK;
362         }
363
364         /* should not be reached */
365         talloc_free(global);
366         return NT_STATUS_INTERNAL_ERROR;
367 }
368
369 static NTSTATUS smbXsrv_open_global_store(struct smbXsrv_connection *sconn,
370                                              struct smbXsrv_open_global0 *global)
371 {
372         struct smbXsrv_open_globalB global_blob;
373         DATA_BLOB blob = data_blob_null;
374         TDB_DATA val;
375         NTSTATUS status;
376         enum ndr_err_code ndr_err;
377
378         /*
379          * TODO: if we use other versions than '0'
380          * we would add glue code here, that would be able to
381          * store the information in the old format.
382          */
383
384         if (global->db_rec == NULL) {
385                 return NT_STATUS_INTERNAL_ERROR;
386         }
387
388         val = dbwrap_record_get_value(global->db_rec);
389
390         ZERO_STRUCT(global_blob);
391         global_blob.version = 0;
392         if (val.dsize >= 8) {
393                 global_blob.seqnum = IVAL(val.dptr, 4);
394         }
395         global_blob.seqnum += 1;
396         global_blob.info.info0 = global;
397
398         DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
399         NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
400
401         ndr_err = ndr_push_struct_blob(&blob, global->db_rec, &global_blob,
402                         (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_globalB);
403         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
404                 //status = ndr_err_code;
405                 TALLOC_FREE(global->db_rec);
406                 //status =  map...
407                 return status;
408         }
409
410         val = make_tdb_data(blob.data, blob.length);
411         status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
412         TALLOC_FREE(global->db_rec);
413         if (!NT_STATUS_IS_OK(status)) {
414                 return status;
415         }
416
417         return NT_STATUS_OK;
418 }
419
420 struct smbXsrv_open_global_fetch_state {
421         TALLOC_CTX *mem_ctx;
422         struct smbXsrv_open_global0 *op;
423         NTSTATUS status;
424 };
425
426 static void smbXsrv_open_global_fetch_parser(TDB_DATA key, TDB_DATA data,
427                                                void *private_data)
428 {
429         struct smbXsrv_open_global_fetch_state *state =
430                 (struct smbXsrv_open_global_fetch_state *)private_data;
431         DATA_BLOB blob = data_blob_const(data.dptr, data.dsize);
432         struct smbXsrv_open_globalB global_blob;
433         enum ndr_err_code ndr_err;
434
435         ndr_err = ndr_pull_struct_blob(&blob, state->mem_ctx, &global_blob,
436                         (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
437         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
438                 //state->status = map;
439                 return;
440         }
441
442         DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
443         NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
444
445         state->op = global_blob.info.info0;
446
447         state->status = NT_STATUS_OK;
448 }
449
450 static NTSTATUS smbXsrv_open_global_lookup(struct smbXsrv_open_table *table,
451                                            uint32_t open_global_id,
452                                            TALLOC_CTX *mem_ctx,
453                                            struct smbXsrv_open_global0 **_open)
454 {
455         struct smbXsrv_open_global_fetch_state state = {
456                 .mem_ctx = mem_ctx,
457                 .op = NULL,
458                 .status = NT_STATUS_INTERNAL_ERROR,
459         };
460         TDB_DATA key;
461         uint8_t key_buf[sizeof(uint32_t)];
462         NTSTATUS status;
463
464         *_open = NULL;
465
466         DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
467
468         if (table->global.db_ctx == NULL) {
469         DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
470                 return NT_STATUS_INTERNAL_ERROR;
471         }
472
473         /* TODO: key as string */
474         RSIVAL(key_buf, 0, open_global_id);
475         key = make_tdb_data(key_buf, sizeof(key_buf));
476
477         status = dbwrap_parse_record(table->global.db_ctx, key,
478                                      smbXsrv_open_global_fetch_parser, &state);
479         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
480         DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
481                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
482         } else if (!NT_STATUS_IS_OK(status)) {
483         DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
484                 return status;
485         }
486         if (!NT_STATUS_IS_OK(state.status)) {
487         DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
488                 return state.status;
489         }
490
491         *_open = state.op;
492         DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
493         return NT_STATUS_OK;
494 }
495
496 static int smbXsrv_open_destructor(struct smbXsrv_open *op)
497 {
498         struct smbXsrv_open_table *table;
499         struct db_record *local_rec = NULL;
500         struct db_record *global_rec = NULL;
501         NTSTATUS status;
502
503         if (op->connection == NULL) {
504                 return 0;
505         }
506
507         table = &op->connection->open_table;
508         op->connection = NULL;
509
510         local_rec = op->db_rec;
511         op->db_rec = NULL;
512         if (local_rec == NULL) {
513                 uint8_t key_buf[sizeof(uint32_t)];
514                 TDB_DATA key;
515
516                 RSIVAL(key_buf, 0, op->local_id);
517                 key = make_tdb_data(key_buf, sizeof(key_buf));
518
519                 local_rec = dbwrap_fetch_locked(table->local.db_ctx,
520                                                 op, key);
521         }
522
523         if (local_rec != NULL) {
524                 status = dbwrap_record_delete(local_rec);
525         }
526
527         global_rec = op->global->db_rec;
528         op->global->db_rec = NULL;
529         if (global_rec == NULL && !op->global->durable) {
530                 uint8_t key_buf[sizeof(uint32_t)];
531                 TDB_DATA key;
532
533                 RSIVAL(key_buf, 0, op->global->open_global_id);
534                 key = make_tdb_data(key_buf, sizeof(key_buf));
535
536                 global_rec = dbwrap_fetch_locked(table->global.db_ctx,
537                                                  op->global, key);
538         }
539
540         if (global_rec != NULL) {
541                 status = dbwrap_record_delete(global_rec);
542         }
543         TALLOC_FREE(op->global);
544
545         return 0;
546 }
547
548 NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn,
549                              NTTIME now,
550                              struct smbXsrv_open **_open)
551 {
552         struct smbXsrv_open_table *table = &conn->open_table;
553         uint32_t max_opens = table->local.highest_id - table->local.lowest_id;
554         struct db_record *local_rec = NULL;
555         struct smbXsrv_open *op = NULL;
556         void *ptr = NULL;
557         TDB_DATA val;
558         struct smbXsrv_open_global0 *global = NULL;
559         NTSTATUS status;
560
561         //system("sleep 999999");
562
563         if (table->local.num_opens >= max_opens) {
564                 return NT_STATUS_INSUFFICIENT_RESOURCES;
565                 // TODO smb1 return NT_STATUS_TOO_MANY_SESSIONS;
566         }
567
568         op = talloc_zero(conn, struct smbXsrv_open);
569         if (op == NULL) {
570                 return NT_STATUS_NO_MEMORY;
571         }
572         op->connection = conn;
573
574         status = smbXsrv_open_global_allocate(table->global.db_ctx,
575                                               op, &global);
576         if (!NT_STATUS_IS_OK(status)) {
577                 talloc_free(open);
578                 return status;
579         }
580         op->global = global;
581
582         talloc_set_destructor(op, smbXsrv_open_destructor);
583
584         status = smbXsrv_open_local_allocate_id(table->local.db_ctx,
585                                                 table->local.lowest_id,
586                                                 table->local.highest_id,
587                                                 op,
588                                                 &local_rec,
589                                                 &op->local_id);
590         if (!NT_STATUS_IS_OK(status)) {
591                 return status;
592         }
593
594         {
595                 uint64_t id = global->open_global_id;
596
597                 global->open_persistent_id = id;
598                 global->open_persistent_id |= (id << 32) & 0xFFFFFFFF00000000ULL;
599         }
600         {
601                 uint64_t id = op->local_id;
602
603                 global->open_volatile_id = id;
604                 //global->open_volatile_id |= (id << 32) & 0xFFFFFFFF00000000ULL;
605         }
606
607         ptr = op;
608         val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr));
609         status = dbwrap_record_store(local_rec, val, TDB_REPLACE);
610         TALLOC_FREE(local_rec);
611         if (!NT_STATUS_IS_OK(status)) {
612                 talloc_free(op);
613                 return status;
614         }
615
616         global->open_time = now;
617         global->open_path = "TODO...";
618
619         status = smbXsrv_open_global_store(conn, global);
620         if (!NT_STATUS_IS_OK(status)) {
621                 talloc_free(op);
622                 return status;
623         }
624
625         {
626                 struct smbXsrv_openB open_blob;
627
628                 ZERO_STRUCT(open_blob);
629                 open_blob.version = 0;
630                 open_blob.info.info0 = op;
631
632                 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
633                 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
634         }
635
636         *_open = op;
637         return NT_STATUS_OK;
638 }
639
640 NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op)
641 {
642         struct smbXsrv_open_table *table = &op->connection->open_table;
643         NTSTATUS status;
644         uint8_t key_buf[sizeof(uint32_t)];
645         TDB_DATA key;
646
647         if (op->global->db_rec != NULL) {
648                 return NT_STATUS_INTERNAL_ERROR;
649         }
650
651         RSIVAL(key_buf, 0, op->global->open_global_id);
652         key = make_tdb_data(key_buf, sizeof(key_buf));
653
654         op->global->db_rec = dbwrap_fetch_locked(table->global.db_ctx,
655                                                  op->global, key);
656         if (op->global->db_rec == NULL) {
657                 // TODO proper return code?
658                 return NT_STATUS_NO_MEMORY;
659         }
660
661         status = smbXsrv_open_global_store(op->connection,
662                                            op->global);
663         if (!NT_STATUS_IS_OK(status)) {
664                 /* debug */
665                 return status;
666         }
667
668         {
669                 struct smbXsrv_openB open_blob;
670
671                 ZERO_STRUCT(open_blob);
672                 open_blob.version = 0;
673                 open_blob.info.info0 = op;
674
675                 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
676                 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
677         }
678
679         return NT_STATUS_OK;
680 }
681
682 NTSTATUS smb1srv_open_table_init(struct smbXsrv_connection *conn)
683 {
684         return smbXsrv_open_table_init(conn, 1,
685                                        conn->sconn->real_max_open_files);
686 }
687
688 NTSTATUS smb1srv_open_lookup(struct smbXsrv_open_table *table,
689                              uint16_t fnum, NTTIME now,
690                              struct smbXsrv_open **_open)
691 {
692         uint32_t local_id = fnum;
693
694         return smbXsrv_open_local_lookup(table, local_id, now, _open);
695 }
696
697 NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn)
698 {
699         return smbXsrv_open_table_init(conn, 1,
700                                        conn->sconn->real_max_open_files);
701 }
702
703 NTSTATUS smb2srv_open_lookup(struct smbXsrv_open_table *table,
704                              uint64_t volatile_id, NTTIME now,
705                              struct smbXsrv_open **_open)
706 {
707         uint32_t local_id = volatile_id & UINT32_MAX;
708
709         return smbXsrv_open_local_lookup(table, local_id, now, _open);
710 }
711
712 NTSTATUS smb2srv_open_global_lookup(struct smbXsrv_open_table *table,
713                                     uint64_t persistent_id,
714                                     TALLOC_CTX *mem_ctx,
715                                     struct smbXsrv_open_global0 **_open)
716 {
717         uint32_t global_id = persistent_id & UINT32_MAX;
718
719         return smbXsrv_open_global_lookup(table, global_id, mem_ctx, _open);
720 }
721