Coverage Report

Created: 2023-05-28 06:42

/src/netcdf-c/libnczarr/zattr.c
Line
Count
Source (jump to first uncovered line)
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 This file handles ZARR attributes.
8
 *
9
 * @author Dennis Heimbigner, Ed Hartnett
10
 */
11
12
#include "zincludes.h"
13
#include "zfilter.h"
14
15
#undef ADEBUG
16
17
/**
18
 * @internal Get the attribute list for either a varid or NC_GLOBAL
19
 *
20
 * @param grp Group
21
 * @param varid Variable ID | NC_BLOGAL
22
 * @param varp Pointer that gets pointer to NC_VAR_INFO_T
23
 * instance. Ignored if NULL.
24
 * @param attlist Pointer that gets pointer to attribute list.
25
 *
26
 * @return NC_NOERR No error.
27
 * @author Dennis Heimbigner, Ed Hartnett
28
 * [Candidate for moving to libsrc4]
29
 */
30
int
31
ncz_getattlist(NC_GRP_INFO_T *grp, int varid, NC_VAR_INFO_T **varp, NCindex **attlist)
32
0
{
33
0
    int retval;
34
0
    NC_FILE_INFO_T* file = grp->nc4_info;
35
0
    NCZ_FILE_INFO_T* zinfo = file->format_file_info;
36
37
0
    assert(grp && attlist && file && zinfo);
38
39
0
    if (varid == NC_GLOBAL)
40
0
    {
41
        /* Do we need to read the atts? */
42
0
        if (!grp->atts_read)
43
0
            if ((retval = ncz_read_atts(file, (NC_OBJ*)grp)))
44
0
                return retval;
45
46
0
        if (varp)
47
0
            *varp = NULL;
48
0
        *attlist = grp->att;
49
0
    }
50
0
    else
51
0
    {
52
0
        NC_VAR_INFO_T *var;
53
54
0
        if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
55
0
            return NC_ENOTVAR;
56
0
        assert(var->hdr.id == varid);
57
58
        /* Do we need to read the atts? */
59
0
        if (!var->atts_read)
60
0
            if ((retval = ncz_read_atts(file, (NC_OBJ*)var)))
61
0
                return retval;
62
63
0
        if (varp)
64
0
            *varp = var;
65
0
        *attlist = var->att;
66
0
    }
67
0
    return NC_NOERR;
68
0
}
69
70
/**
71
 * @internal Get one of the special attributes:
72
 * See the reserved attribute table in libsrc4/nc4internal.c.
73
 * The special attributes are the ones marked with NAMEONLYFLAG.
74
 * For example: NCPROPS, ISNETCDF4ATT, and SUPERBLOCKATT, and CODECS.
75
 * These atts are not all really in the file, they are constructed on the fly.
76
 *
77
 * @param h5 Pointer to ZARR file info struct.
78
 * @param var Pointer to var info struct; NULL signals global.
79
 * @param name Name of attribute.
80
 * @param filetypep Pointer that gets type of the attribute data in
81
 * file.
82
 * @param mem_type Type of attribute data in memory.
83
 * @param lenp Pointer that gets length of attribute array.
84
 * @param attnump Pointer that gets the attribute number.
85
 * @param data Attribute data.
86
 *
87
 * @return ::NC_NOERR No error.
88
 * @return ::NC_EBADID Bad ncid.
89
 * @return ::NC_ERANGE Data conversion out of range.
90
 * @author Dennis Heimbigner
91
 */
92
int
93
ncz_get_att_special(NC_FILE_INFO_T* h5, NC_VAR_INFO_T* var, const char* name,
94
                    nc_type* filetypep, nc_type mem_type, size_t* lenp,
95
                    int* attnump, void* data)
96
0
{
97
0
    int stat = NC_NOERR;
98
    
99
    /* Fail if asking for att id */
100
0
    if(attnump)
101
0
        {stat = NC_EATTMETA; goto done;}
102
103
    /* Handle the per-var case(s) first */
104
0
    if(var != NULL) {
105
0
#ifdef ENABLE_NCZARR_FILTERS
106
0
        if(strcmp(name,NC_ATT_CODECS)==0) { 
107
0
            NClist* filters = (NClist*)var->filters;
108
109
0
            if(mem_type == NC_NAT) mem_type = NC_CHAR;
110
0
            if(mem_type != NC_CHAR)
111
0
                {stat = NC_ECHAR; goto done;}
112
0
            if(filetypep) *filetypep = NC_CHAR;
113
0
      if(lenp) *lenp = 0;
114
0
      if(filters == NULL) goto done;   
115
0
      if((stat = NCZ_codec_attr(var,lenp,data))) goto done;
116
0
  }
117
0
#endif
118
0
  goto done;
119
0
    }
120
121
    /* The global reserved attributes */
122
0
    if(strcmp(name,NCPROPS)==0) {
123
0
        int len;
124
0
        if(h5->provenance.ncproperties == NULL)
125
0
            {stat = NC_ENOTATT; goto done;}
126
0
        if(mem_type == NC_NAT) mem_type = NC_CHAR;
127
0
        if(mem_type != NC_CHAR)
128
0
            {stat = NC_ECHAR; goto done;}
129
0
        if(filetypep) *filetypep = NC_CHAR;
130
0
  len = strlen(h5->provenance.ncproperties);
131
0
        if(lenp) *lenp = len;
132
0
        if(data) strncpy((char*)data,h5->provenance.ncproperties,len+1);
133
0
    } else if(strcmp(name,ISNETCDF4ATT)==0
134
0
              || strcmp(name,SUPERBLOCKATT)==0) {
135
0
        unsigned long long iv = 0;
136
0
        if(filetypep) *filetypep = NC_INT;
137
0
        if(lenp) *lenp = 1;
138
0
        if(strcmp(name,SUPERBLOCKATT)==0)
139
0
            iv = (unsigned long long)h5->provenance.superblockversion;
140
0
        else /* strcmp(name,ISNETCDF4ATT)==0 */
141
0
            iv = NCZ_isnetcdf4(h5);
142
0
        if(mem_type == NC_NAT) mem_type = NC_INT;
143
0
        if(data)
144
0
            switch (mem_type) {
145
0
            case NC_BYTE: *((char*)data) = (char)iv; break;
146
0
            case NC_SHORT: *((short*)data) = (short)iv; break;
147
0
            case NC_INT: *((int*)data) = (int)iv; break;
148
0
            case NC_UBYTE: *((unsigned char*)data) = (unsigned char)iv; break;
149
0
            case NC_USHORT: *((unsigned short*)data) = (unsigned short)iv; break;
150
0
            case NC_UINT: *((unsigned int*)data) = (unsigned int)iv; break;
151
0
            case NC_INT64: *((long long*)data) = (long long)iv; break;
152
0
            case NC_UINT64: *((unsigned long long*)data) = (unsigned long long)iv; break;
153
0
            default:
154
0
                {stat = NC_ERANGE; goto done;}
155
0
            }
156
0
    }
157
0
done:
158
0
    return stat;
159
160
0
}
161
162
/**
163
 * @internal I think all atts should be named the exact same thing, to
164
 * avoid confusion!
165
 *
166
 * @param ncid File and group ID.
167
 * @param varid Variable ID.
168
 * @param name Name of attribute.
169
 * @param newname New name for attribute.
170
 *
171
 * @return ::NC_NOERR No error.
172
 * @return ::NC_EBADID Bad ncid.
173
 * @return ::NC_EMAXNAME New name too long.
174
 * @return ::NC_EPERM File is read-only.
175
 * @return ::NC_ENAMEINUSE New name already in use.
176
 * @return ::NC_ENOTINDEFINE Classic model file not in define mode.
177
 * @return ::NC_EHDFERR HDF error.
178
 * @return ::NC_ENOMEM Out of memory.
179
 * @return ::NC_EINTERNAL Could not rebuild list.
180
 * @author Dennis Heimbigner, Ed Hartnett
181
 */
182
int
183
NCZ_rename_att(int ncid, int varid, const char *name, const char *newname)
184
0
{
185
0
    NC_GRP_INFO_T *grp;
186
0
    NC_FILE_INFO_T *h5;
187
0
    NC_VAR_INFO_T *var = NULL;
188
0
    NC_ATT_INFO_T *att;
189
0
    NCindex *list;
190
0
    char norm_newname[NC_MAX_NAME + 1], norm_name[NC_MAX_NAME + 1];
191
0
    int retval = NC_NOERR;
192
193
0
    if (!name || !newname)
194
0
        return NC_EINVAL;
195
196
0
    LOG((2, "nc_rename_att: ncid 0x%x varid %d name %s newname %s",
197
0
         ncid, varid, name, newname));
198
199
    /* If the new name is too long, that's an error. */
200
0
    if (strlen(newname) > NC_MAX_NAME)
201
0
        return NC_EMAXNAME;
202
203
    /* Find info for this file, group, and h5 info. */
204
0
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
205
0
        return retval;
206
0
    assert(h5 && grp);
207
208
    /* If the file is read-only, return an error. */
209
0
    if (h5->no_write)
210
0
        return NC_EPERM;
211
212
    /* Check and normalize the name. */
213
0
    if ((retval = nc4_check_name(newname, norm_newname)))
214
0
        return retval;
215
216
    /* Get the list of attributes. */
217
0
    if ((retval = ncz_getattlist(grp, varid, &var, &list)))
218
0
        return retval;
219
220
    /* Is new name in use? */
221
0
    att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_newname);
222
0
    if(att != NULL)
223
0
        return NC_ENAMEINUSE;
224
225
    /* Normalize name and find the attribute. */
226
0
    if ((retval = nc4_normalize_name(name, norm_name)))
227
0
        return retval;
228
229
0
    att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_name);
230
0
    if (!att)
231
0
        return NC_ENOTATT;
232
233
    /* If we're not in define mode, new name must be of equal or
234
       less size, if complying with strict NC3 rules. */
235
0
    if (!(h5->flags & NC_INDEF) && strlen(norm_newname) > strlen(att->hdr.name) &&
236
0
        (h5->cmode & NC_CLASSIC_MODEL))
237
0
        return NC_ENOTINDEFINE;
238
239
    /* Copy the new name into our metadata. */
240
0
    if(att->hdr.name) free(att->hdr.name);
241
0
    if (!(att->hdr.name = strdup(norm_newname)))
242
0
        return NC_ENOMEM;
243
244
0
    att->dirty = NC_TRUE;
245
246
    /* Rehash the attribute list so that the new name is used */
247
0
    if(!ncindexrebuild(list))
248
0
        return NC_EINTERNAL;
249
250
    /* Mark attributes on variable dirty, so they get written */
251
0
    if(var)
252
0
        var->attr_dirty = NC_TRUE;
253
0
    return retval;
254
0
}
255
256
/**
257
 * @internal Delete an att. Rub it out. Push the button on
258
 * it. Liquidate it. Bump it off. Take it for a one-way
259
 * ride. Terminate it.
260
 *
261
 * @param ncid File and group ID.
262
 * @param varid Variable ID.
263
 * @param name Name of attribute to delete.
264
 *
265
 * @return ::NC_NOERR No error.
266
 * @return ::NC_EBADID Bad ncid.
267
 * @return ::NC_ENOTATT Attribute not found.
268
 * @return ::NC_EINVAL No name provided.
269
 * @return ::NC_EPERM File is read only.
270
 * @return ::NC_ENOTINDEFINE Classic model not in define mode.
271
 * @return ::NC_EINTERNAL Could not rebuild list.
272
 * @author Dennis Heimbigner, Ed Hartnett
273
 */
274
int
275
NCZ_del_att(int ncid, int varid, const char *name)
276
0
{
277
0
    NC_GRP_INFO_T *grp;
278
0
    NC_VAR_INFO_T *var;
279
0
    NC_FILE_INFO_T *h5;
280
0
    NC_ATT_INFO_T *att;
281
0
    NCindex* attlist = NULL;
282
0
    int i;
283
0
    size_t deletedid;
284
0
    int retval;
285
286
    /* Name must be provided. */
287
0
    if (!name)
288
0
        return NC_EINVAL;
289
290
0
    LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name));
291
292
    /* Find info for this file, group, and h5 info. */
293
0
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
294
0
        return retval;
295
0
    assert(h5 && grp);
296
297
    /* If the file is read-only, return an error. */
298
0
    if (h5->no_write)
299
0
        return NC_EPERM;
300
301
    /* If file is not in define mode, return error for classic model
302
     * files, otherwise switch to define mode. */
303
0
    if (!(h5->flags & NC_INDEF))
304
0
    {
305
0
        if (h5->cmode & NC_CLASSIC_MODEL)
306
0
            return NC_ENOTINDEFINE;
307
0
        if ((retval = NCZ_redef(ncid)))
308
0
            return retval;
309
0
    }
310
311
    /* Get either the global or a variable attribute list. */
312
0
    if ((retval = ncz_getattlist(grp, varid, &var, &attlist)))
313
0
        return retval;
314
315
#ifdef LOOK
316
    /* Determine the location id in the ZARR file. */
317
    if (varid == NC_GLOBAL)
318
        locid = ((NCZ_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid;
319
    else if (var->created)
320
        locid = ((NCZ_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid;
321
#endif
322
323
    /* Now find the attribute by name. */
324
0
    if (!(att = (NC_ATT_INFO_T*)ncindexlookup(attlist, name)))
325
0
        return NC_ENOTATT;
326
327
    /* Reclaim the content of the attribute */
328
0
    if(att->data) {
329
0
  if((retval = nc_reclaim_data_all(ncid,att->nc_typeid,att->data,att->len))) return retval;
330
0
    }
331
0
    att->data = NULL;
332
0
    att->len = 0;
333
334
    /* Delete it from the ZARR file, if it's been created. */
335
0
    if (att->created)
336
0
    {
337
#ifdef LOOK
338
        assert(locid);
339
        if (H5Adelete(locid, att->hdr.name) < 0)
340
            return NC_EATTMETA;
341
#endif
342
0
    }
343
344
0
    deletedid = att->hdr.id;
345
346
    /* reclaim associated NCZarr info */
347
0
    {
348
0
  NCZ_ATT_INFO_T* za = (NCZ_ATT_INFO_T*)att->format_att_info;
349
0
  nullfree(za);
350
0
    }
351
352
    /* Remove this attribute in this list */
353
0
    if ((retval = nc4_att_list_del(attlist, att)))
354
0
        return retval;
355
356
    /* Renumber all attributes with higher indices. */
357
0
    for (i = 0; i < ncindexsize(attlist); i++)
358
0
    {
359
0
        NC_ATT_INFO_T *a;
360
0
        if (!(a = (NC_ATT_INFO_T *)ncindexith(attlist, i)))
361
0
            continue;
362
0
        if (a->hdr.id > deletedid)
363
0
            a->hdr.id--;
364
0
    }
365
366
    /* Rebuild the index. */
367
0
    if (!ncindexrebuild(attlist))
368
0
        return NC_EINTERNAL;
369
370
0
    return NC_NOERR;
371
0
}
372
373
/**
374
 * @internal This will return the length of a netcdf atomic data type
375
 * in bytes.
376
 *
377
 * @param type A netcdf atomic type.
378
 *
379
 * @return Type size in bytes, or -1 if type not found.
380
 * @author Dennis Heimbigner, Ed Hartnett
381
 */
382
static int
383
nc4typelen(nc_type type)
384
0
{
385
0
    switch(type){
386
0
    case NC_BYTE:
387
0
    case NC_CHAR:
388
0
    case NC_UBYTE:
389
0
        return 1;
390
0
    case NC_USHORT:
391
0
    case NC_SHORT:
392
0
        return 2;
393
0
    case NC_FLOAT:
394
0
    case NC_INT:
395
0
    case NC_UINT:
396
0
        return 4;
397
0
    case NC_DOUBLE:
398
0
    case NC_INT64:
399
0
    case NC_UINT64:
400
0
        return 8;
401
0
    }
402
0
    return -1;
403
0
}
404
405
/**
406
 * @internal
407
 * Write an attribute to a netCDF-4/NCZ file, converting
408
 * data type if necessary.
409
 *
410
 * @param ncid File and group ID.
411
 * @param varid Variable ID.
412
 * @param name Name of attribute.
413
 * @param file_type Type of the attribute data in file.
414
 * @param len Number of elements in attribute array.
415
 * @param data Attribute data.
416
 * @param mem_type Type of data in memory.
417
 * @param force write even if the attribute is special
418
 *
419
 * @return ::NC_NOERR No error.
420
 * @return ::NC_EINVAL Invalid parameters.
421
 * @return ::NC_EBADID Bad ncid.
422
 * @return ::NC_ENOTVAR Variable not found.
423
 * @return ::NC_EBADNAME Name contains illegal characters.
424
 * @return ::NC_ENAMEINUSE Name already in use.
425
 * @author Dennis Heimbigner, Ed Hartnett
426
 */
427
int
428
ncz_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type,
429
            size_t len, const void *data, nc_type mem_type, int force)
430
0
{
431
0
    NC* nc;
432
0
    NC_FILE_INFO_T *h5 = NULL;
433
0
    NC_VAR_INFO_T *var = NULL;
434
0
    NCindex* attlist = NULL;
435
0
    NC_ATT_INFO_T* att;
436
0
    char norm_name[NC_MAX_NAME + 1];
437
0
    nc_bool_t new_att = NC_FALSE;
438
0
    int retval = NC_NOERR, range_error = 0;
439
0
    size_t type_size;
440
0
    int ret;
441
0
    int ncid;
442
0
    void* copy = NULL;
443
    /* Save the old att data and length and old fillvalue in case we need to rollback on error */
444
0
    struct Save {
445
0
  size_t len;
446
0
  void* data;
447
0
        nc_type type; /* In case we change the type of the attribute */
448
0
    } attsave = {0,NULL,-1};
449
0
    struct Save fillsave = {0,NULL,-1};
450
451
0
    h5 = grp->nc4_info;
452
0
    nc = h5->controller;
453
0
    assert(nc && grp && h5);
454
455
0
    ncid = nc->ext_ncid | grp->hdr.id;
456
457
    /* Find att, if it exists. (Must check varid first or nc_test will
458
     * break.) This also does lazy att reads if needed. */
459
0
    if ((ret = ncz_getattlist(grp, varid, &var, &attlist)))
460
0
        return ret;
461
462
    /* The length needs to be positive (cast needed for braindead
463
       systems with signed size_t). */
464
0
    if((unsigned long) len > X_INT_MAX)
465
0
        return NC_EINVAL;
466
467
    /* Check name before LOG statement. */
468
0
    if (!name || strlen(name) > NC_MAX_NAME)
469
0
        return NC_EBADNAME;
470
471
0
    LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d",
472
0
         __func__,ncid, varid, name, file_type, mem_type, len));
473
474
    /* If len is not zero, then there must be some data. */
475
0
    if (len && !data)
476
0
        return NC_EINVAL;
477
478
    /* If the file is read-only, return an error. */
479
0
    if (h5->no_write)
480
0
        return NC_EPERM;
481
482
    /* Check and normalize the name. */
483
0
    if ((retval = nc4_check_name(name, norm_name)))
484
0
        return retval;
485
486
    /* Check that a reserved att name is not being used improperly */
487
0
    const NC_reservedatt* ra = NC_findreserved(name);
488
0
    if(ra != NULL && !force) {
489
        /* case 1: grp=root, varid==NC_GLOBAL, flags & READONLYFLAG */
490
0
        if (nc->ext_ncid == ncid && varid == NC_GLOBAL && grp->parent == NULL
491
0
            && (ra->flags & READONLYFLAG))
492
0
            return NC_ENAMEINUSE;
493
        /* case 2: grp=NA, varid!=NC_GLOBAL, flags & HIDDENATTRFLAG */
494
0
        if (varid != NC_GLOBAL && (ra->flags & HIDDENATTRFLAG))
495
0
            return NC_ENAMEINUSE;
496
0
    }
497
498
    /* See if there is already an attribute with this name. */
499
0
    att = (NC_ATT_INFO_T*)ncindexlookup(attlist,norm_name);
500
501
0
    if (!att)
502
0
    {
503
        /* If this is a new att, require define mode. */
504
0
        if (!(h5->flags & NC_INDEF))
505
0
        {
506
507
0
            if (h5->cmode & NC_CLASSIC_MODEL)
508
0
                return NC_ENOTINDEFINE;
509
0
            if ((retval = NCZ_redef(ncid)))
510
0
                BAIL(retval);
511
0
        }
512
0
        new_att = NC_TRUE;
513
0
    }
514
0
    else
515
0
    {
516
        /* For an existing att, if we're not in define mode, the len
517
           must not be greater than the existing len for classic model. */
518
0
        if (!(h5->flags & NC_INDEF) &&
519
0
            len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid))
520
0
        {
521
0
            if (h5->cmode & NC_CLASSIC_MODEL)
522
0
                return NC_ENOTINDEFINE;
523
0
            if ((retval = NCZ_redef(ncid)))
524
0
                BAIL(retval);
525
0
        }
526
0
    }
527
528
    /* We must have two valid types to continue. */
529
0
    if (file_type == NC_NAT || mem_type == NC_NAT)
530
0
        return NC_EBADTYPE;
531
532
    /* No character conversions are allowed. */
533
0
    if (file_type != mem_type &&
534
0
        (file_type == NC_CHAR || mem_type == NC_CHAR ||
535
0
         file_type == NC_STRING || mem_type == NC_STRING))
536
0
        return NC_ECHAR;
537
538
    /* For classic mode file, only allow atts with classic types to be
539
     * created. */
540
0
    if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE)
541
0
        return NC_ESTRICTNC3;
542
543
    /* Add to the end of the attribute list, if this att doesn't
544
       already exist. */
545
0
    if (new_att)
546
0
    {
547
0
        LOG((3, "adding attribute %s to the list...", norm_name));
548
0
        if ((ret = nc4_att_list_add(attlist, norm_name, &att)))
549
0
            BAIL(ret);
550
551
        /* Allocate storage for the ZARR specific att info. */
552
0
        if (!(att->format_att_info = calloc(1, sizeof(NCZ_ATT_INFO_T))))
553
0
            BAIL(NC_ENOMEM);
554
555
0
  if(varid == NC_GLOBAL)
556
0
      att->container = (NC_OBJ*)grp;
557
0
  else
558
0
      att->container = (NC_OBJ*)var;
559
0
    }
560
561
    /* Now fill in the metadata. */
562
0
    att->dirty = NC_TRUE;
563
564
    /* When we reclaim existing data, make sure to use the right type */ 
565
0
    if(new_att) attsave.type = file_type; else attsave.type = att->nc_typeid;
566
0
    att->nc_typeid = file_type;
567
568
    /* Get information about this (possibly new) type. */
569
0
    if ((retval = nc4_get_typelen_mem(h5, file_type, &type_size)))
570
0
        return retval;
571
572
0
    if (att->data)
573
0
    {
574
0
  assert(attsave.data == NULL);
575
0
  attsave.data = att->data;
576
0
  attsave.len = att->len;
577
0
        att->data = NULL;
578
0
    }
579
580
    /* If this is the _FillValue attribute, then we will also have to
581
     * copy the value to the fill_value pointer of the NC_VAR_INFO_T
582
     * struct for this var. (But ignore a global _FillValue
583
     * attribute). Also kill the cache fillchunk as no longer valid */
584
0
    if (!strcmp(att->hdr.name, _FillValue) && varid != NC_GLOBAL)
585
0
    {
586
        /* Fill value must have exactly one value */
587
0
        if (len != 1)
588
0
            return NC_EINVAL;
589
590
        /* If we already wrote to the dataset, then return an error. */
591
0
        if (var->written_to)
592
0
            return NC_ELATEFILL;
593
594
        /* Get the length of the veriable data type. */
595
0
        if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id,
596
0
                                          &type_size)))
597
0
            return retval;
598
599
        /* Already set a fill value? Now I'll have to free the old
600
         * one. Make up your damn mind, would you? */
601
0
        if (var->fill_value)
602
0
        {
603
      /* reclaim later */
604
0
      fillsave.data = var->fill_value;
605
0
      fillsave.type = var->type_info->hdr.id;
606
0
      fillsave.len = 1;
607
0
      var->fill_value = NULL;
608
0
        }
609
610
        /* Determine the size of the fill value in bytes. */
611
  
612
0
  {
613
0
      nc_type var_type = var->type_info->hdr.id;
614
0
        size_t var_type_size = var->type_info->size;
615
      /* The old code used the var's type as opposed to the att's type; normally same,
616
         but not required. Now we need to convert from the att's type to the var's type.
617
         Note that we use mem_type rather than file_type because our data is in the form
618
         of the memory data. When we later capture the memory data for the actual
619
         attribute, we will use file_type as the target of the conversion. */
620
0
      if(mem_type != var_type && mem_type < NC_STRING && var_type < NC_STRING) {
621
    /* Need to convert from memory data into copy buffer */
622
0
    if((copy = malloc(len*var_type_size))==NULL) BAIL(NC_ENOMEM);
623
0
                if ((retval = nc4_convert_type(data, copy, mem_type, var_type,
624
0
                                               len, &range_error, NULL,
625
0
                                               (h5->cmode & NC_CLASSIC_MODEL),
626
0
                 NC_NOQUANTIZE, 0)))
627
0
                    BAIL(retval);
628
0
      } else { /* no conversion */
629
    /* Still need a copy of the input data */
630
0
    copy = NULL;
631
0
          if((retval = nc_copy_data_all(h5->controller->ext_ncid, mem_type, data, 1, &copy)))
632
0
        BAIL(retval);
633
0
      }
634
0
      var->fill_value = copy;
635
0
      copy = NULL;
636
0
  }
637
638
        /* Indicate that the fill value was changed, if the variable has already
639
         * been created in the file, so the dataset gets deleted and re-created. */
640
0
        if (var->created)
641
0
            var->fill_val_changed = NC_TRUE;
642
        /* Reclaim any existing fill_chunk */
643
0
        if((retval = NCZ_reclaim_fill_chunk(((NCZ_VAR_INFO_T*)var->format_var_info)->cache))) BAIL(retval);
644
0
    }
645
646
    /* Copy the attribute data, if there is any. */
647
0
    if (len)
648
0
    {
649
0
        nc_type type_class;    /* Class of attribute's type */
650
651
        /* Get class for this type. */
652
0
        if ((retval = nc4_get_typeclass(h5, file_type, &type_class)))
653
0
            return retval;
654
655
0
        assert(data);
656
0
        {
657
      /* Allocate top level of the copy */
658
0
      if (!(copy = malloc(len * type_size)))
659
0
                BAIL(NC_ENOMEM);
660
      /* Special case conversion from memory to file type */
661
0
      if(mem_type != file_type && mem_type < NC_STRING && file_type < NC_STRING) {
662
0
                if ((retval = nc4_convert_type(data, copy, mem_type, file_type,
663
0
                                               len, &range_error, NULL,
664
0
                                               (h5->cmode & NC_CLASSIC_MODEL),
665
0
                 NC_NOQUANTIZE, 0)))
666
0
                    BAIL(retval);
667
0
      } else if(mem_type == file_type) { /* General case: no conversion */
668
0
          if((retval = nc_copy_data(h5->controller->ext_ncid,file_type,data,len,copy)))
669
0
        BAIL(retval);
670
0
      } else
671
0
        BAIL(NC_EURL);
672
      /* Store it */
673
0
      att->data = copy; copy = NULL;
674
0
  }
675
0
    }
676
677
    /* If this is a maxstrlen attribute, then we will also have to
678
     * sync the value to NCZ_VAR_INFO_T or NCZ_FILE_INFO_T structure */
679
0
    {
680
0
  if(strcmp(att->hdr.name,NC_NCZARR_DEFAULT_MAXSTRLEN_ATTR)==0 && varid == NC_GLOBAL && len == 1) {
681
0
      NCZ_FILE_INFO_T* zfile = (NCZ_FILE_INFO_T*)h5->format_file_info;
682
0
      if((retval = nc4_convert_type(att->data, &zfile->default_maxstrlen, file_type, NC_INT,
683
0
              len, &range_error, NULL, NC_CLASSIC_MODEL, NC_NOQUANTIZE, 0)))
684
0
          BAIL(retval);
685
0
  } else if(strcmp(att->hdr.name,NC_NCZARR_MAXSTRLEN_ATTR)==0 && varid != NC_GLOBAL && len == 1) {
686
0
      NCZ_VAR_INFO_T* zvar = (NCZ_VAR_INFO_T*)var->format_var_info;
687
0
  if((retval = nc4_convert_type(att->data, &zvar->maxstrlen, file_type, NC_INT,
688
0
              len, &range_error, NULL, NC_CLASSIC_MODEL, NC_NOQUANTIZE, 0)))
689
0
          BAIL(retval);
690
0
  }
691
0
    }
692
693
0
    att->dirty = NC_TRUE;
694
0
    att->created = NC_FALSE;
695
0
    att->len = len;
696
    
697
    /* Mark attributes on variable dirty, so they get written */
698
0
    if(var)
699
0
        var->attr_dirty = NC_TRUE;
700
    /* Reclaim saved data */
701
0
    if(attsave.data != NULL) {
702
0
        assert(attsave.len > 0);
703
0
        (void)nc_reclaim_data_all(h5->controller->ext_ncid,attsave.type,attsave.data,attsave.len);
704
0
  attsave.len = 0; attsave.data = NULL;
705
0
    }
706
0
    if(fillsave.data != NULL) {
707
0
        assert(fillsave.len > 0);
708
0
        (void)nc_reclaim_data_all(h5->controller->ext_ncid,fillsave.type,fillsave.data,fillsave.len);
709
0
  fillsave.len = 0; fillsave.data = NULL;
710
0
    }
711
712
0
exit:
713
0
    if(copy)
714
0
        (void)nc_reclaim_data_all(h5->controller->ext_ncid,file_type,copy,len);
715
0
    if(retval) {
716
  /* Rollback */
717
0
        if(attsave.data != NULL) {
718
0
            assert(attsave.len > 0);
719
0
      if(att->data)
720
0
                (void)nc_reclaim_data_all(h5->controller->ext_ncid,attsave.type,att->data,att->len);
721
0
      att->len = attsave.len; att->data = attsave.data;
722
0
        }
723
0
        if(fillsave.data != NULL) {
724
0
            assert(fillsave.len > 0);
725
0
      if(att->data)
726
0
      (void)nc_reclaim_data_all(h5->controller->ext_ncid,fillsave.type,var->fill_value,1);
727
0
      var->fill_value = fillsave.data;
728
0
        }
729
0
    }    
730
    /* If there was an error return it, otherwise return any potential
731
       range error value. If none, return NC_NOERR as usual.*/
732
0
    if (range_error)
733
0
        return NC_ERANGE;
734
0
    if (retval)
735
0
        return retval;
736
0
    return NC_NOERR;
737
0
}
738
739
/**
740
 * @internal Write an attribute to a netCDF-4/NCZ file, converting
741
 * data type if necessary.
742
 *
743
 * @param ncid File and group ID.
744
 * @param varid Variable ID.
745
 * @param name Name of attribute.
746
 * @param file_type Type of the attribute data in file.
747
 * @param len Number of elements in attribute array.
748
 * @param data Attribute data.
749
 * @param mem_type Type of data in memory.
750
 *
751
 * @return ::NC_NOERR No error.
752
 * @return ::NC_EINVAL Invalid parameters.
753
 * @return ::NC_EBADID Bad ncid.
754
 * @return ::NC_ENOTVAR Variable not found.
755
 * @return ::NC_EBADNAME Name contains illegal characters.
756
 * @return ::NC_ENAMEINUSE Name already in use.
757
 * @author Dennis Heimbigner, Ed Hartnett
758
 */
759
int
760
NCZ_put_att(int ncid, int varid, const char *name, nc_type file_type,
761
                 size_t len, const void *data, nc_type mem_type)
762
0
{
763
0
    NC_FILE_INFO_T *h5;
764
0
    NC_GRP_INFO_T *grp;
765
0
    int ret;
766
767
    /* Find info for this file, group, and h5 info. */
768
0
    if ((ret = nc4_find_grp_h5(ncid, &grp, &h5)))
769
0
        return ret;
770
0
    assert(grp && h5);
771
772
0
    return ncz_put_att(grp, varid, name, file_type, len, data, mem_type, 0);
773
0
}
774
775
/**
776
 * @internal Learn about an att. All the nc4 nc_inq_ functions just
777
 * call ncz_get_att to get the metadata on an attribute.
778
 *
779
 * @param ncid File and group ID.
780
 * @param varid Variable ID.
781
 * @param name Name of attribute.
782
 * @param xtypep Pointer that gets type of attribute.
783
 * @param lenp Pointer that gets length of attribute data array.
784
 *
785
 * @return ::NC_NOERR No error.
786
 * @return ::NC_EBADID Bad ncid.
787
 * @author Dennis Heimbigner, Ed Hartnett
788
 */
789
int
790
NCZ_inq_att(int ncid, int varid, const char *name, nc_type *xtypep,
791
                 size_t *lenp)
792
0
{
793
0
    NC_FILE_INFO_T *h5;
794
0
    NC_GRP_INFO_T *grp;
795
0
    NC_VAR_INFO_T *var = NULL;
796
0
    char norm_name[NC_MAX_NAME + 1];
797
0
    int retval;
798
799
0
    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
800
801
    /* Find the file, group, and var info, and do lazy att read if
802
     * needed. */
803
0
    if ((retval = ncz_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
804
0
                                            &h5, &grp, &var, NULL)))
805
0
        return retval;
806
807
    /* If this is one of the reserved atts, use nc_get_att_special. */
808
0
    {
809
0
        const NC_reservedatt *ra = NC_findreserved(norm_name);
810
0
        if (ra  && ra->flags & NAMEONLYFLAG)
811
0
            return ncz_get_att_special(h5, var, norm_name, xtypep, NC_NAT, lenp, NULL,
812
0
                                       NULL);
813
0
    }
814
815
0
    return nc4_get_att_ptrs(h5, grp, var, norm_name, xtypep, NC_NAT,
816
0
                            lenp, NULL, NULL);
817
0
}
818
819
/**
820
 * @internal Learn an attnum, given a name.
821
 *
822
 * @param ncid File and group ID.
823
 * @param varid Variable ID.
824
 * @param name Name of attribute.
825
 * @param attnump Pointer that gets the attribute index number.
826
 *
827
 * @return ::NC_NOERR No error.
828
 * @author Dennis Heimbigner, Ed Hartnett
829
 */
830
int
831
NCZ_inq_attid(int ncid, int varid, const char *name, int *attnump)
832
0
{
833
0
    NC_FILE_INFO_T *h5;
834
0
    NC_GRP_INFO_T *grp;
835
0
    NC_VAR_INFO_T *var = NULL;
836
0
    char norm_name[NC_MAX_NAME + 1];
837
0
    int retval;
838
839
0
    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
840
841
    /* Find the file, group, and var info, and do lazy att read if
842
     * needed. */
843
0
    if ((retval = ncz_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
844
0
                                            &h5, &grp, &var, NULL)))
845
0
        return retval;
846
847
    /* If this is one of the reserved atts, use nc_get_att_special. */
848
0
    {
849
0
        const NC_reservedatt *ra = NC_findreserved(norm_name);
850
0
        if (ra  && ra->flags & NAMEONLYFLAG)
851
0
            return ncz_get_att_special(h5, var, norm_name, NULL, NC_NAT, NULL, attnump,
852
0
                                       NULL);
853
0
    }
854
855
0
    return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, NC_NAT,
856
0
                            NULL, attnump, NULL);
857
0
}
858
859
/**
860
 * @internal Given an attnum, find the att's name.
861
 *
862
 * @param ncid File and group ID.
863
 * @param varid Variable ID.
864
 * @param attnum The index number of the attribute.
865
 * @param name Pointer that gets name of attrribute.
866
 *
867
 * @return ::NC_NOERR No error.
868
 * @return ::NC_EBADID Bad ncid.
869
 * @author Dennis Heimbigner, Ed Hartnett
870
 */
871
int
872
NCZ_inq_attname(int ncid, int varid, int attnum, char *name)
873
0
{
874
0
    NC_ATT_INFO_T *att;
875
0
    int retval = NC_NOERR;
876
877
0
    ZTRACE(1,"ncid=%d varid=%d attnum=%d",ncid,varid,attnum);
878
0
    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
879
880
    /* Find the file, group, and var info, and do lazy att read if
881
     * needed. */
882
0
    if ((retval = ncz_find_grp_var_att(ncid, varid, NULL, attnum, 0, NULL,
883
0
                                            NULL, NULL, NULL, &att)))
884
0
  goto done;
885
0
    assert(att);
886
887
    /* Get the name. */
888
0
    if (name)
889
0
        strcpy(name, att->hdr.name);
890
0
done:
891
0
    return ZUNTRACEX(retval,"name=%s",(retval?"":name));
892
0
}
893
894
/**
895
 * @internal Get an attribute.
896
 *
897
 * @param ncid File and group ID.
898
 * @param varid Variable ID.
899
 * @param name Name of attribute.
900
 * @param value Pointer that gets attribute data.
901
 * @param memtype The type the data should be converted to as it is
902
 * read.
903
 *
904
 * @return ::NC_NOERR No error.
905
 * @return ::NC_EBADID Bad ncid.
906
 * @author Dennis Heimbigner, Ed Hartnett
907
 */
908
int
909
NCZ_get_att(int ncid, int varid, const char *name, void *value,
910
                 nc_type memtype)
911
0
{
912
0
    NC_FILE_INFO_T *h5;
913
0
    NC_GRP_INFO_T *grp;
914
0
    NC_VAR_INFO_T *var = NULL;
915
0
    char norm_name[NC_MAX_NAME + 1];
916
0
    int retval;
917
918
0
    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
919
920
    /* Find the file, group, and var info, and do lazy att read if
921
     * needed. */
922
0
    if ((retval = ncz_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
923
0
                                            &h5, &grp, &var, NULL)))
924
0
        return retval;
925
926
    /* If this is one of the reserved global atts, use nc_get_att_special. */
927
0
    {
928
0
        const NC_reservedatt *ra = NC_findreserved(norm_name);
929
0
        if (ra  && ra->flags & NAMEONLYFLAG)
930
0
            return ncz_get_att_special(h5, var, norm_name, NULL, NC_NAT, NULL, NULL,
931
0
                                       value);
932
0
    }
933
934
0
    return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, memtype,
935
0
                            NULL, NULL, value);
936
0
}
937
938
#if 0
939
static int
940
ncz_del_attr(NC_FILE_INFO_T* file, NC_OBJ* container, const char* name)
941
{
942
    int i,stat = NC_NOERR;
943
944
    ZTRACE();
945
946
    if(container->sort == NCGRP)
947
  stat = ncz_getattlist((NC_GRP_INFO_T*)container,NC_GLOBAL,NULL,&attlist);
948
    else
949
  stat = ncz_getattlist((NC_VAR_INFO_T*)container,NC_GLOBAL,NULL,&attlist);
950
951
  goto done;
952
953
    /* Iterate over the attributes to locate the matching attribute */
954
    for(i=0;i<nclistlength(jattrs->dict);i+=2) {
955
  NCjson* key = nclistget(jattrs->dict,i);
956
  assert(key->sort == NCJ_STRING);
957
  if(strcmp(key->value,name)==0) {
958
      /* Remove and reclaim */
959
      NCjson* value = nclistget(jattrs->dict,i+1);
960
      nclistremove(jattrs->dict,i);
961
      nclistremove(jattrs->dict,i+1);
962
      NCJreclaim(key);
963
      NCJreclaim(value);
964
      break;
965
  }    
966
    }
967
    /* Write the json back out */
968
    if((stat = ncz_unload_jatts(zinfo, container, jattrs, jtypes)))
969
  goto done;
970
971
done:
972
    NCJreclaim(jattrs);
973
    NCJreclaim(jtypes);
974
    return stat;
975
}
976
#endif
977
978
/* If we do not have a _FillValue, then go ahead and create it */
979
int
980
ncz_create_fillvalue(NC_VAR_INFO_T* var)
981
0
{
982
0
    int stat = NC_NOERR;
983
0
    int i;
984
0
    NC_ATT_INFO_T* fv = NULL;
985
986
    /* Have the var's attributes been read? */
987
0
    if(!var->atts_read) goto done; /* above my pay grade */
988
989
    /* Is FillValue warranted? */
990
0
    if(!var->no_fill && var->fill_value != NULL) {
991
        /* Make sure _FillValue does not exist */
992
0
  for(i=0;i<ncindexsize(var->att);i++) {
993
0
      fv = (NC_ATT_INFO_T*)ncindexith(var->att,i);
994
0
      if(strcmp(fv->hdr.name,_FillValue)==0) break;
995
0
      fv = NULL;
996
0
        }
997
0
  if(fv == NULL) {
998
      /* Create it */
999
0
      if((stat = ncz_makeattr((NC_OBJ*)var,var->att,_FillValue,var->type_info->hdr.id,1,var->fill_value,&fv)))
1000
0
      goto done;
1001
0
  }
1002
0
    }
1003
0
done:
1004
0
    return THROW(stat);
1005
0
}
1006
1007
/* Create an attribute; This is an abbreviated form
1008
   of ncz_put_att above */
1009
int
1010
ncz_makeattr(NC_OBJ* container, NCindex* attlist, const char* name, nc_type typeid, size_t len, void* values, NC_ATT_INFO_T** attp)
1011
0
{
1012
0
    int stat = NC_NOERR;
1013
0
    NC_ATT_INFO_T* att = NULL;
1014
0
    NCZ_ATT_INFO_T* zatt = NULL;
1015
0
    void* clone = NULL;
1016
0
    size_t typesize, clonesize;
1017
0
    int ncid;
1018
0
    NC* nc = NULL;
1019
0
    NC_GRP_INFO_T* grp = (container->sort == NCGRP ? (NC_GRP_INFO_T*)container
1020
0
                                                   : ((NC_VAR_INFO_T*)container)->container);
1021
1022
0
    nc = grp->nc4_info->controller;
1023
0
    ncid = nc->ext_ncid | grp->hdr.id;
1024
1025
    /* Duplicate the values */
1026
0
    if ((stat = nc4_get_typelen_mem(grp->nc4_info, typeid, &typesize))) goto done;
1027
0
    clonesize = len*typesize;
1028
0
    if((clone = malloc(clonesize))==NULL) {stat = NC_ENOMEM; goto done;}
1029
0
    if((stat = nc_copy_data(ncid, typeid, values, len, clone))) goto done;
1030
1031
0
    if((stat=nc4_att_list_add(attlist,name,&att)))
1032
0
  goto done;
1033
0
    if((zatt = calloc(1,sizeof(NCZ_ATT_INFO_T))) == NULL)
1034
0
  {stat = NC_ENOMEM; goto done;}
1035
0
    if(container->sort == NCGRP) {
1036
0
        zatt->common.file = ((NC_GRP_INFO_T*)container)->nc4_info;
1037
0
    } else if(container->sort == NCVAR) {
1038
0
        zatt->common.file = ((NC_VAR_INFO_T*)container)->container->nc4_info;
1039
0
    } else
1040
0
  abort();
1041
0
    att->container = container;
1042
0
    att->format_att_info = zatt;
1043
    /* Fill in the attribute's type and value  */
1044
0
    att->nc_typeid = typeid;
1045
0
    att->len = len;
1046
0
    att->data = clone; clone = NULL;
1047
0
    att->dirty = NC_TRUE;
1048
0
    if(attp) {*attp = att; att = NULL;}
1049
1050
0
done:
1051
0
    nullfree(clone);
1052
0
    if(stat) {
1053
0
  if(att) nc4_att_list_del(attlist,att);
1054
0
  nullfree(zatt);
1055
0
    }
1056
0
    return THROW(stat);
1057
0
}
1058