Coverage Report

Created: 2025-10-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/netcdf-c/libnczarr/zfile.c
Line
Count
Source
1
/* Copyright 2003-2018, University Corporation for Atmospheric
2
 * Research. See COPYRIGHT file for copying and redistribution
3
 * conditions. */
4
5
/**
6
 * @file
7
 * @internal The netCDF-4 file functions.
8
 *
9
 * This file is part of netcdf-4, a netCDF-like interface for NCZ, or
10
 * a ZARR backend for netCDF, depending on your point of view.
11
 *
12
 * @author Dennis Heimbigner, Ed Hartnett
13
 */
14
15
#include "zincludes.h"
16
#include "zfilter.h"
17
#include <stddef.h>
18
19
/* Forward */
20
static int NCZ_enddef(NC_FILE_INFO_T* h5);
21
static int ncz_sync_netcdf4_file(NC_FILE_INFO_T* file, int isclose);
22
23
/**
24
 * @internal Put the file back in redef mode. This is done
25
 * automatically for netcdf-4 files, if the user forgets.
26
 *
27
 * @param ncid File and group ID.
28
 *
29
 * @return ::NC_NOERR No error.
30
 * @author Dennis Heimbigner, Ed Hartnett
31
 */
32
int
33
NCZ_redef(int ncid)
34
0
{
35
0
    NC_FILE_INFO_T* zinfo = NULL;
36
0
    int stat = NC_NOERR;
37
38
0
    ZTRACE(0,"NCZ_redef(ncid)");
39
40
    /* Find this file's metadata. */
41
0
    if ((stat = nc4_find_grp_h5(ncid, NULL, &zinfo)))
42
0
        goto done;
43
0
    assert(zinfo);
44
45
    /* If we're already in define mode, return an error. */
46
0
    if (zinfo->flags & NC_INDEF)
47
0
        {stat = NC_EINDEFINE; goto done;}
48
49
    /* If the file is read-only, return an error. */
50
0
    if (zinfo->no_write)
51
0
        {stat = NC_EPERM; goto done;}
52
53
    /* Set define mode. */
54
0
    zinfo->flags |= NC_INDEF;
55
56
    /* For nc_abort, we need to remember if we're in define mode as a
57
       redef. */
58
0
    zinfo->redef = NC_TRUE;
59
60
0
done:
61
0
    return ZUNTRACE(stat);
62
0
}
63
64
/**
65
 * @internal For netcdf-4 files, this just calls nc_enddef, ignoring
66
 * the extra parameters.
67
 *
68
 * @param ncid File and group ID.
69
 * @param h_minfree Ignored for netCDF-4 files.
70
 * @param v_align Ignored for netCDF-4 files.
71
 * @param v_minfree Ignored for netCDF-4 files.
72
 * @param r_align Ignored for netCDF-4 files.
73
 *
74
 * @return ::NC_NOERR No error.
75
 * @author Dennis Heimbigner, Ed Hartnett
76
 */
77
int
78
NCZ__enddef(int ncid, size_t h_minfree, size_t v_align,
79
            size_t v_minfree, size_t r_align)
80
0
{
81
0
    int stat = NC_NOERR;
82
0
    NC_FILE_INFO_T* h5 = NULL;
83
0
    NC_GRP_INFO_T* grp = NULL;
84
0
    ZTRACE(0,"ncid=%d",ncid);
85
0
    if ((stat = nc4_find_grp_h5(ncid, &grp, &h5)))
86
0
        goto done;
87
0
    stat = NCZ_enddef(h5);
88
0
done:
89
0
    return ZUNTRACE(stat);
90
0
}
91
92
/**
93
 * @internal Take the file out of define mode. This is called
94
 * automatically for netcdf-4 files, if the user forgets.
95
 *
96
 * @param h5 File object
97
 *
98
 * @return ::NC_NOERR No error.
99
 * @return ::NC_EBADID Bad ncid.
100
 * @return ::NC_EBADGRPID Bad group ID.
101
 * @author Dennis Heimbigner, Ed Hartnett
102
 */
103
static int
104
NCZ_enddef(NC_FILE_INFO_T* h5)
105
0
{
106
0
    NC_VAR_INFO_T *var;
107
0
    size_t i,j;
108
0
    int stat = NC_NOERR;
109
110
0
    ZTRACE(1,"h5=%s",h5->hdr.name);
111
112
    /* When exiting define mode, process all variables */
113
0
    for (i = 0; i < nclistlength(h5->allgroups); i++) { 
114
0
  NC_GRP_INFO_T* g = nclistget(h5->allgroups,i);
115
0
        for (j = 0; j < ncindexsize(g->vars); j++) {
116
0
            var = (NC_VAR_INFO_T *)ncindexith(g->vars, j);
117
0
            assert(var);
118
0
            var->written_to = NC_TRUE; /* mark it written */
119
0
      var->created = 1;
120
0
        }
121
0
    }
122
0
    if((stat = ncz_enddef_netcdf4_file(h5))) goto done;
123
0
done:
124
0
    return ZUNTRACE(stat);
125
0
}
126
127
/**
128
 * @internal Flushes all buffers associated with the file, after
129
 * writing all changed metadata. This may only be called in data mode.
130
 *
131
 * @param ncid File and group ID.
132
 *
133
 * @return ::NC_NOERR No error.
134
 * @return ::NC_EBADID Bad ncid.
135
 * @return ::NC_EINDEFINE Classic model file is in define mode.
136
 * @author Dennis Heimbigner, Ed Hartnett
137
 */
138
int
139
NCZ_sync(int ncid)
140
0
{
141
0
    int stat = NC_NOERR;
142
0
    NC_FILE_INFO_T* file = NULL;
143
144
0
    ZTRACE(0,"ncid=%d",ncid);
145
146
0
    LOG((2, "%s: ncid 0x%x", __func__, ncid));
147
148
0
    if ((stat = nc4_find_grp_h5(ncid, NULL, &file)))
149
0
        return stat;
150
0
    assert(file);
151
152
    /* If we're in define mode, we can't sync. */
153
0
    if (file->flags & NC_INDEF)
154
0
    {
155
0
        if (file->cmode & NC_CLASSIC_MODEL)
156
0
            return NC_EINDEFINE;
157
0
        if ((stat = NCZ_enddef(file)))
158
0
            return stat;
159
0
    }
160
161
    /* do not do this if file is writeonce */
162
0
    stat = ncz_sync_netcdf4_file(file,!ZCLOSE);
163
0
    return stat;
164
0
}
165
166
/**
167
 * @internal From the netcdf-3 docs: The function nc_abort just closes
168
 * the netCDF dataset, if not in define mode. If the dataset is being
169
 * created and is still in define mode, the dataset is deleted. If
170
 * define mode was entered by a call to nc_redef, the netCDF dataset
171
 * is restored to its state before definition mode was entered and the
172
 * dataset is closed.
173
 *
174
 * @param ncid File and group ID.
175
 *
176
 * @return ::NC_NOERR No error.
177
 * @author Dennis Heimbigner, Ed Hartnett
178
 */
179
int
180
NCZ_abort(int ncid)
181
0
{
182
0
    int stat = NC_NOERR;
183
0
    NC_FILE_INFO_T* h5 = NULL;
184
0
    ZTRACE(0,"ncid=%d",ncid);
185
0
    LOG((2, "%s: ncid 0x%x", __func__, ncid));
186
    /* Find metadata for this file. */
187
0
    if ((stat = nc4_find_grp_h5(ncid, NULL, &h5)))
188
0
        return stat;
189
0
    assert(h5);
190
0
    stat = ncz_closeorabort(h5, NULL, 1);
191
0
    return ZUNTRACE(stat);
192
0
}
193
194
/**
195
 * @internal Close the netcdf file, writing any changes first.
196
 *
197
 * @param ncid File and group ID.
198
 * @param params any extra parameters in/out of close
199
 *
200
 * @return ::NC_NOERR No error.
201
 * @author Dennis Heimbigner, Ed Hartnett
202
 */
203
int
204
NCZ_close(int ncid, void* params)
205
0
{
206
0
    int stat = NC_NOERR;
207
0
    NC_FILE_INFO_T* h5 = NULL;
208
209
0
    ZTRACE(0,"ncid=%d",ncid);
210
0
    LOG((1, "%s: ncid 0x%x", __func__, ncid));
211
    /* Find metadata for this file. */
212
0
    if ((stat = nc4_find_grp_h5(ncid, NULL, &h5)))
213
0
        return stat;
214
0
    assert(h5);
215
0
    return ncz_closeorabort(h5, params, 0);
216
0
}
217
218
/**
219
 * @internal From the netcdf-3 docs: The function nc_abort just closes
220
 * the netCDF dataset, if not in define mode. If the dataset is being
221
 * created and is still in define mode, the dataset is deleted. If
222
 * define mode was entered by a call to nc_redef, the netCDF dataset
223
 * is restored to its state before definition mode was entered and the
224
 * dataset is closed.
225
 *
226
 * @param ncid File and group ID.
227
 *
228
 * @return ::NC_NOERR No error.
229
 * @author Dennis Heimbigner, Ed Hartnett
230
 */
231
int
232
ncz_closeorabort(NC_FILE_INFO_T* h5, void* params, int abort)
233
0
{
234
0
    int stat = NC_NOERR;
235
236
0
    assert(h5);
237
238
0
    NC_UNUSED(params);
239
240
0
    ZTRACE(3,"file=%s abort=%d",h5->hdr.name,abort);
241
242
0
    LOG((2, "%s: file: %p", __func__, h5));
243
244
    /* If we're in define mode, but not redefing the file, delete it. */
245
0
    if(!abort) {
246
  /* Invoke enddef if needed, which includes sync first */
247
0
  if(h5->flags & NC_INDEF) h5->flags ^= NC_INDEF;
248
  /* Sync the file unless this is a read-only file. */
249
0
  if(!h5->no_write) {
250
0
      if((stat = ncz_sync_netcdf4_file(h5,ZCLOSE)))
251
0
    goto done;
252
0
  }
253
0
    }
254
255
    /* Reclaim memory */
256
257
    /* Free any zarr-related data, including the map */
258
0
    if ((stat = ncz_close_file(h5, abort)))
259
0
  goto done;
260
261
    /* Reclaim provenance info */
262
0
    NCZ_clear_provenance(&h5->provenance);
263
264
    /* Free the NC_FILE_INFO_T struct. */
265
0
    if ((stat = nc4_nc4f_list_del(h5)))
266
0
        return stat;
267
268
0
done:
269
0
    return ZUNTRACE(stat);
270
0
}
271
272
/**************************************************/
273
/**
274
 * @internal Learn number of dimensions, variables, global attributes,
275
 * and the ID of the first unlimited dimension (if any).
276
 *
277
 * @note It's possible for any of these pointers to be NULL, in which
278
 * case don't try to figure out that value.
279
 *
280
 * @param ncid File and group ID.
281
 * @param ndimsp Pointer that gets number of dimensions.
282
 * @param nvarsp Pointer that gets number of variables.
283
 * @param nattsp Pointer that gets number of global attributes.
284
 * @param unlimdimidp Pointer that gets first unlimited dimension ID,
285
 * or -1 if there are no unlimied dimensions.
286
 *
287
 * @return ::NC_NOERR No error.
288
 * @author Dennis Heimbigner, Ed Hartnett
289
 */
290
int
291
NCZ_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
292
0
{
293
0
    NC *nc;
294
0
    NC_FILE_INFO_T* file;
295
0
    NC_GRP_INFO_T *grp;
296
0
    int stat = NC_NOERR;
297
0
    size_t i;
298
299
0
    LOG((2, "%s: ncid 0x%x", __func__, ncid));
300
301
    /* Find file metadata. */
302
0
    if ((stat = nc4_find_nc_grp_h5(ncid, &nc, &grp, &file)))
303
0
        return stat;
304
305
0
    assert(file && grp && nc);
306
307
    /* Count the number of dims, vars, and global atts; need to iterate
308
     * because of possible nulls. */
309
0
    if (ndimsp)
310
0
    {
311
0
        *ndimsp = ncindexcount(grp->dim);
312
0
    }
313
0
    if (nvarsp)
314
0
    {
315
0
        *nvarsp = ncindexcount(grp->vars);
316
0
    }
317
0
    if (nattsp)
318
0
    {
319
        /* Do we need to read the atts? */
320
0
        if (!grp->atts_read)
321
0
            if ((stat = ncz_read_atts(file,(NC_OBJ*)grp)))
322
0
                return stat;
323
324
0
        *nattsp = ncindexcount(grp->att);
325
0
    }
326
327
0
    if (unlimdimidp)
328
0
    {
329
        /* Default, no unlimited dimension */
330
0
        *unlimdimidp = -1;
331
332
        /* If there's more than one unlimited dim, which was not possible
333
           with netcdf-3, then only the last unlimited one will be reported
334
           back in xtendimp. */
335
        /* Note that this code is inconsistent with nc_inq_unlimid() */
336
0
        for(i=0;i<ncindexsize(grp->dim);i++) {
337
0
            NC_DIM_INFO_T* d = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
338
0
            if(d == NULL) continue;
339
0
            if(d->unlimited) {
340
0
                *unlimdimidp = d->hdr.id;
341
0
                break;
342
0
            }
343
0
        }
344
0
    }
345
346
0
    return NC_NOERR;
347
0
}
348
349
/**
350
 * @internal This function will write all changed metadata and flush
351
 * ZARR file to disk.
352
 *
353
 * @param file Pointer to file info struct.
354
 *
355
 * @return ::NC_NOERR No error.
356
 * @return ::NC_EINDEFINE Classic model file in define mode.
357
 * @return ::NC_EHDFERR ZARR error.
358
 * @author Dennis Heimbigner, Ed Hartnett
359
 */
360
361
static int
362
ncz_sync_netcdf4_file(NC_FILE_INFO_T* file, int isclose)
363
0
{
364
0
    int stat = NC_NOERR;
365
366
0
    assert(file && file->format_file_info);
367
0
    LOG((3, "%s", __func__));
368
0
    ZTRACE(2,"file=%s",file->hdr.name);
369
370
    /* End depend mode if needed. (Error checking for classic mode has
371
     * already happened). */
372
0
    if (file->flags & NC_INDEF)
373
0
    {
374
        /* Turn define mode off. */
375
0
        file->flags ^= NC_INDEF;
376
377
        /* Redef mode needs to be tracked separately for nc_abort. */
378
0
        file->redef = NC_FALSE;
379
0
    }
380
381
#ifdef LOGGING
382
    /* This will print out the names, types, lens, etc of the vars and
383
       atts in the file, if the logging level is 2 or greater. */
384
    log_metadata_nc(file);
385
#endif
386
387
    /* Write any metadata that has changed. */
388
0
    if (!file->no_write)
389
0
    {
390
        /* Write out provenance; will create _NCProperties */
391
0
        if((stat = NCZ_write_provenance(file)))
392
0
            goto done;
393
394
        /* Write all the metadata. */
395
0
  if((stat = ncz_sync_file(file,isclose)))
396
0
      goto done;
397
0
    }
398
0
done:
399
0
    return ZUNTRACE(stat);
400
0
}
401
402
/**
403
 * @internal This function will do the enddef stuff for an nczarr file.
404
 *
405
 * @param file Pointer to ZARR file info struct.
406
 *
407
 * @return ::NC_NOERR No error.
408
 * @return ::NC_ENOTINDEFINE Not in define mode.
409
 * @author Dennis Heimbigner, Ed Hartnett
410
 */
411
int
412
ncz_enddef_netcdf4_file(NC_FILE_INFO_T* file)
413
0
{
414
0
    assert(file);
415
0
    LOG((3, "%s", __func__));
416
417
    /* If we're not in define mode, return an error. */
418
0
    if (!(file->flags & NC_INDEF))
419
0
        return NC_ENOTINDEFINE;
420
421
    /* Turn define mode off. */
422
0
    file->flags ^= NC_INDEF;
423
424
    /* Redef mode needs to be tracked separately for nc_abort. */
425
0
    file->redef = NC_FALSE;
426
427
0
    return ncz_sync_netcdf4_file(file,!ZCLOSE);
428
0
}
429
430
/**
431
 * @internal IN netcdf, you first create
432
 * the variable and then (optionally) specify the fill value.
433
 *
434
 * @param ncid File and group ID.
435
 * @param fillmode File mode.
436
 * @param old_modep Pointer that gets old mode. Ignored if NULL.
437
 *
438
 * @return ::NC_NOERR No error.
439
 * @author Dennis Heimbigner
440
 */
441
int
442
NCZ_set_fill(int ncid, int fillmode, int *old_modep)
443
0
{
444
0
    NC_FILE_INFO_T* h5 = NULL;
445
0
    int stat = NC_NOERR;
446
447
0
    ZTRACE(0,"NCZ_set_fill(ncid,fillmode,old)");
448
449
    /* Get pointer to file info. */
450
0
    if ((stat = nc4_find_grp_h5(ncid, NULL, &h5)))
451
0
        goto done;
452
0
    assert(h5);
453
454
    /* Trying to set fill on a read-only file? You sicken me! */
455
0
    if (h5->no_write)
456
0
        {stat = NC_EPERM; goto done;}
457
458
    /* Did you pass me some weird fillmode? */
459
0
    if (fillmode != NC_FILL && fillmode != NC_NOFILL)
460
0
        {stat = NC_EINVAL; goto done;}
461
462
    /* If the user wants to know, tell him what the old mode was. */
463
0
    if (old_modep)
464
0
        *old_modep = h5->fill_mode;
465
466
0
    h5->fill_mode = fillmode;
467
468
0
done:
469
0
    return ZUNTRACE(stat);
470
0
}