Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/netcdf-c-4.7.4/libhdf5/hdf5attr.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
 * @file
6
 * @internal This file handles HDF5 attributes.
7
 *
8
 * @author Ed Hartnett
9
 */
10
11
#include "config.h"
12
#include "hdf5internal.h"
13
14
/**
15
 * @internal Get the attribute list for either a varid or NC_GLOBAL
16
 *
17
 * @param grp Group
18
 * @param varid Variable ID | NC_BLOGAL
19
 * @param varp Pointer that gets pointer to NC_VAR_INFO_T
20
 * instance. Ignored if NULL.
21
 * @param attlist Pointer that gets pointer to attribute list.
22
 *
23
 * @return NC_NOERR No error.
24
 * @author Dennis Heimbigner, Ed Hartnett
25
 */
26
static int
27
getattlist(NC_GRP_INFO_T *grp, int varid, NC_VAR_INFO_T **varp,
28
           NCindex **attlist)
29
137k
{
30
137k
    int retval;
31
32
137k
    assert(grp && attlist);
33
34
137k
    if (varid == NC_GLOBAL)
35
113k
    {
36
        /* Do we need to read the atts? */
37
113k
        if (!grp->atts_read)
38
0
            if ((retval = nc4_read_atts(grp, NULL)))
39
0
                return retval;
40
41
113k
        if (varp)
42
113k
            *varp = NULL;
43
113k
        *attlist = grp->att;
44
113k
    }
45
23.3k
    else
46
23.3k
    {
47
23.3k
        NC_VAR_INFO_T *var;
48
49
23.3k
        if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
50
0
            return NC_ENOTVAR;
51
23.3k
        assert(var->hdr.id == varid);
52
53
        /* Do we need to read the atts? */
54
23.3k
        if (!var->atts_read)
55
0
            if ((retval = nc4_read_atts(grp, var)))
56
0
                return retval;
57
58
23.3k
        if (varp)
59
23.3k
            *varp = var;
60
23.3k
        *attlist = var->att;
61
23.3k
    }
62
137k
    return NC_NOERR;
63
137k
}
64
65
/**
66
 * @internal Get one of three special attributes, NCPROPS,
67
 * ISNETCDF4ATT, and SUPERBLOCKATT. These atts are not all really in
68
 * the file, they are constructed on the fly.
69
 *
70
 * @param h5 Pointer to HDF5 file info struct.
71
 * @param name Name of attribute.
72
 * @param filetypep Pointer that gets type of the attribute data in
73
 * file.
74
 * @param mem_type Type of attribute data in memory.
75
 * @param lenp Pointer that gets length of attribute array.
76
 * @param attnump Pointer that gets the attribute number.
77
 * @param data Attribute data.
78
 *
79
 * @return ::NC_NOERR No error.
80
 * @return ::NC_EBADID Bad ncid.
81
 * @return ::NC_ERANGE Data conversion out of range.
82
 * @author Dennis Heimbigner
83
 */
84
int
85
nc4_get_att_special(NC_FILE_INFO_T* h5, const char* name,
86
                    nc_type* filetypep, nc_type mem_type, size_t* lenp,
87
                    int* attnump, void* data)
88
0
{
89
    /* Fail if asking for att id */
90
0
    if(attnump)
91
0
        return NC_EATTMETA;
92
93
0
    if(strcmp(name,NCPROPS)==0) {
94
0
        int len;
95
0
        if(h5->provenance.ncproperties == NULL)
96
0
            return NC_ENOTATT;
97
0
        if(mem_type == NC_NAT) mem_type = NC_CHAR;
98
0
        if(mem_type != NC_CHAR)
99
0
            return NC_ECHAR;
100
0
        if(filetypep) *filetypep = NC_CHAR;
101
0
  len = strlen(h5->provenance.ncproperties);
102
0
        if(lenp) *lenp = len;
103
0
        if(data) strncpy((char*)data,h5->provenance.ncproperties,len+1);
104
0
    } else if(strcmp(name,ISNETCDF4ATT)==0
105
0
              || strcmp(name,SUPERBLOCKATT)==0) {
106
0
        unsigned long long iv = 0;
107
0
        if(filetypep) *filetypep = NC_INT;
108
0
        if(lenp) *lenp = 1;
109
0
        if(strcmp(name,SUPERBLOCKATT)==0)
110
0
            iv = (unsigned long long)h5->provenance.superblockversion;
111
0
        else /* strcmp(name,ISNETCDF4ATT)==0 */
112
0
            iv = NC4_isnetcdf4(h5);
113
0
        if(mem_type == NC_NAT) mem_type = NC_INT;
114
0
        if(data)
115
0
            switch (mem_type) {
116
0
            case NC_BYTE: *((char*)data) = (char)iv; break;
117
0
            case NC_SHORT: *((short*)data) = (short)iv; break;
118
0
            case NC_INT: *((int*)data) = (int)iv; break;
119
0
            case NC_UBYTE: *((unsigned char*)data) = (unsigned char)iv; break;
120
0
            case NC_USHORT: *((unsigned short*)data) = (unsigned short)iv; break;
121
0
            case NC_UINT: *((unsigned int*)data) = (unsigned int)iv; break;
122
0
            case NC_INT64: *((long long*)data) = (long long)iv; break;
123
0
            case NC_UINT64: *((unsigned long long*)data) = (unsigned long long)iv; break;
124
0
            default:
125
0
                return NC_ERANGE;
126
0
            }
127
0
    }
128
0
    return NC_NOERR;
129
0
}
130
131
/**
132
 * @internal I think all atts should be named the exact same thing, to
133
 * avoid confusion!
134
 *
135
 * @param ncid File and group ID.
136
 * @param varid Variable ID.
137
 * @param name Name of attribute.
138
 * @param newname New name for attribute.
139
 *
140
 * @return ::NC_NOERR No error.
141
 * @return ::NC_EBADID Bad ncid.
142
 * @return ::NC_EMAXNAME New name too long.
143
 * @return ::NC_EPERM File is read-only.
144
 * @return ::NC_ENAMEINUSE New name already in use.
145
 * @return ::NC_ENOTINDEFINE Classic model file not in define mode.
146
 * @return ::NC_EHDFERR HDF error.
147
 * @return ::NC_ENOMEM Out of memory.
148
 * @return ::NC_EINTERNAL Could not rebuild list.
149
 * @author Ed Hartnett
150
 */
151
int
152
NC4_HDF5_rename_att(int ncid, int varid, const char *name, const char *newname)
153
0
{
154
0
    NC_GRP_INFO_T *grp;
155
0
    NC_FILE_INFO_T *h5;
156
0
    NC_VAR_INFO_T *var = NULL;
157
0
    NC_ATT_INFO_T *att;
158
0
    NCindex *list;
159
0
    char norm_newname[NC_MAX_NAME + 1], norm_name[NC_MAX_NAME + 1];
160
0
    hid_t datasetid = 0;
161
0
    int retval = NC_NOERR;
162
163
0
    if (!name || !newname)
164
0
        return NC_EINVAL;
165
166
0
    LOG((2, "nc_rename_att: ncid 0x%x varid %d name %s newname %s",
167
0
         ncid, varid, name, newname));
168
169
    /* If the new name is too long, that's an error. */
170
0
    if (strlen(newname) > NC_MAX_NAME)
171
0
        return NC_EMAXNAME;
172
173
    /* Find info for this file, group, and h5 info. */
174
0
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
175
0
        return retval;
176
0
    assert(h5 && grp);
177
178
    /* If the file is read-only, return an error. */
179
0
    if (h5->no_write)
180
0
        return NC_EPERM;
181
182
    /* Check and normalize the name. */
183
0
    if ((retval = nc4_check_name(newname, norm_newname)))
184
0
        return retval;
185
186
    /* Get the list of attributes. */
187
0
    if ((retval = getattlist(grp, varid, &var, &list)))
188
0
        return retval;
189
190
    /* Is new name in use? */
191
0
    att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_newname);
192
0
    if(att != NULL)
193
0
        return NC_ENAMEINUSE;
194
195
    /* Normalize name and find the attribute. */
196
0
    if ((retval = nc4_normalize_name(name, norm_name)))
197
0
        return retval;
198
199
0
    att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_name);
200
0
    if (!att)
201
0
        return NC_ENOTATT;
202
203
    /* If we're not in define mode, new name must be of equal or
204
       less size, if complying with strict NC3 rules. */
205
0
    if (!(h5->flags & NC_INDEF) && strlen(norm_newname) > strlen(att->hdr.name) &&
206
0
        (h5->cmode & NC_CLASSIC_MODEL))
207
0
        return NC_ENOTINDEFINE;
208
209
    /* Delete the original attribute, if it exists in the HDF5 file. */
210
0
    if (att->created)
211
0
    {
212
0
        if (varid == NC_GLOBAL)
213
0
        {
214
0
            if (H5Adelete(((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid,
215
0
                          att->hdr.name) < 0)
216
0
                return NC_EHDFERR;
217
0
        }
218
0
        else
219
0
        {
220
0
            if ((retval = nc4_open_var_grp2(grp, varid, &datasetid)))
221
0
                return retval;
222
0
            if (H5Adelete(datasetid, att->hdr.name) < 0)
223
0
                return NC_EHDFERR;
224
0
        }
225
0
        att->created = NC_FALSE;
226
0
    }
227
228
    /* Copy the new name into our metadata. */
229
0
    if(att->hdr.name) free(att->hdr.name);
230
0
    if (!(att->hdr.name = strdup(norm_newname)))
231
0
        return NC_ENOMEM;
232
0
    att->hdr.hashkey = NC_hashmapkey(att->hdr.name,strlen(att->hdr.name)); /* Fix hash key */
233
234
0
    att->dirty = NC_TRUE;
235
236
    /* Rehash the attribute list so that the new name is used */
237
0
    if(!ncindexrebuild(list))
238
0
        return NC_EINTERNAL;
239
240
    /* Mark attributes on variable dirty, so they get written */
241
0
    if(var)
242
0
        var->attr_dirty = NC_TRUE;
243
244
0
    return retval;
245
0
}
246
247
/**
248
 * @internal Delete an att. Rub it out. Push the button on
249
 * it. Liquidate it. Bump it off. Take it for a one-way
250
 * ride. Terminate it.
251
 *
252
 * @param ncid File and group ID.
253
 * @param varid Variable ID.
254
 * @param name Name of attribute to delete.
255
 *
256
 * @return ::NC_NOERR No error.
257
 * @return ::NC_EBADID Bad ncid.
258
 * @return ::NC_ENOTATT Attribute not found.
259
 * @return ::NC_EINVAL No name provided.
260
 * @return ::NC_EPERM File is read only.
261
 * @return ::NC_ENOTINDEFINE Classic model not in define mode.
262
 * @return ::NC_EINTERNAL Could not rebuild list.
263
 * @author Ed Hartnett, Dennis Heimbigner
264
 */
265
int
266
NC4_HDF5_del_att(int ncid, int varid, const char *name)
267
0
{
268
0
    NC_GRP_INFO_T *grp;
269
0
    NC_VAR_INFO_T *var;
270
0
    NC_FILE_INFO_T *h5;
271
0
    NC_ATT_INFO_T *att;
272
0
    NCindex* attlist = NULL;
273
0
    hid_t locid = 0;
274
0
    int i;
275
0
    size_t deletedid;
276
0
    int retval;
277
278
    /* Name must be provided. */
279
0
    if (!name)
280
0
        return NC_EINVAL;
281
282
0
    LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name));
283
284
    /* Find info for this file, group, and h5 info. */
285
0
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
286
0
        return retval;
287
0
    assert(h5 && grp);
288
289
    /* If the file is read-only, return an error. */
290
0
    if (h5->no_write)
291
0
        return NC_EPERM;
292
293
    /* If file is not in define mode, return error for classic model
294
     * files, otherwise switch to define mode. */
295
0
    if (!(h5->flags & NC_INDEF))
296
0
    {
297
0
        if (h5->cmode & NC_CLASSIC_MODEL)
298
0
            return NC_ENOTINDEFINE;
299
0
        if ((retval = NC4_redef(ncid)))
300
0
            return retval;
301
0
    }
302
303
    /* Get either the global or a variable attribute list. */
304
0
    if ((retval = getattlist(grp, varid, &var, &attlist)))
305
0
        return retval;
306
307
    /* Determine the location id in the HDF5 file. */
308
0
    if (varid == NC_GLOBAL)
309
0
        locid = ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid;
310
0
    else if (var->created)
311
0
        locid = ((NC_HDF5_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid;
312
313
    /* Now find the attribute by name. */
314
0
    if (!(att = (NC_ATT_INFO_T*)ncindexlookup(attlist, name)))
315
0
        return NC_ENOTATT;
316
317
    /* Delete it from the HDF5 file, if it's been created. */
318
0
    if (att->created)
319
0
    {
320
0
        assert(locid);
321
0
        if (H5Adelete(locid, att->hdr.name) < 0)
322
0
            return NC_EATTMETA;
323
0
    }
324
325
0
    deletedid = att->hdr.id;
326
327
    /* Remove this attribute in this list */
328
0
    if ((retval = nc4_att_list_del(attlist, att)))
329
0
        return retval;
330
331
    /* Renumber all attributes with higher indices. */
332
0
    for (i = 0; i < ncindexsize(attlist); i++)
333
0
    {
334
0
        NC_ATT_INFO_T *a;
335
0
        if (!(a = (NC_ATT_INFO_T *)ncindexith(attlist, i)))
336
0
            continue;
337
0
        if (a->hdr.id > deletedid)
338
0
            a->hdr.id--;
339
0
    }
340
341
    /* Rebuild the index. */
342
0
    if (!ncindexrebuild(attlist))
343
0
        return NC_EINTERNAL;
344
345
0
    return NC_NOERR;
346
0
}
347
348
/**
349
 * @internal This will return the length of a netcdf atomic data type
350
 * in bytes.
351
 *
352
 * @param type A netcdf atomic type.
353
 *
354
 * @return Type size in bytes, or -1 if type not found.
355
 * @author Ed Hartnett
356
 */
357
static int
358
nc4typelen(nc_type type)
359
0
{
360
0
    switch(type){
361
0
    case NC_BYTE:
362
0
    case NC_CHAR:
363
0
    case NC_UBYTE:
364
0
        return 1;
365
0
    case NC_USHORT:
366
0
    case NC_SHORT:
367
0
        return 2;
368
0
    case NC_FLOAT:
369
0
    case NC_INT:
370
0
    case NC_UINT:
371
0
        return 4;
372
0
    case NC_DOUBLE:
373
0
    case NC_INT64:
374
0
    case NC_UINT64:
375
0
        return 8;
376
0
    }
377
0
    return -1;
378
0
}
379
380
/**
381
 * @internal
382
 * Write an attribute to a netCDF-4/HDF5 file, converting
383
 * data type if necessary.
384
 *
385
 * @param ncid File and group ID.
386
 * @param varid Variable ID.
387
 * @param name Name of attribute.
388
 * @param file_type Type of the attribute data in file.
389
 * @param len Number of elements in attribute array.
390
 * @param data Attribute data.
391
 * @param mem_type Type of data in memory.
392
 * @param force write even if the attribute is special
393
 *
394
 * @return ::NC_NOERR No error.
395
 * @return ::NC_EINVAL Invalid parameters.
396
 * @return ::NC_EBADID Bad ncid.
397
 * @return ::NC_ENOTVAR Variable not found.
398
 * @return ::NC_EBADNAME Name contains illegal characters.
399
 * @return ::NC_ENAMEINUSE Name already in use.
400
 * @author Ed Hartnett, Dennis Heimbigner
401
 */
402
int
403
nc4_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type,
404
            size_t len, const void *data, nc_type mem_type, int force)
405
137k
{
406
137k
    NC* nc;
407
137k
    NC_FILE_INFO_T *h5;
408
137k
    NC_VAR_INFO_T *var = NULL;
409
137k
    NCindex* attlist = NULL;
410
137k
    NC_ATT_INFO_T* att;
411
137k
    char norm_name[NC_MAX_NAME + 1];
412
137k
    nc_bool_t new_att = NC_FALSE;
413
137k
    int retval = NC_NOERR, range_error = 0;
414
137k
    size_t type_size;
415
137k
    int i;
416
137k
    int ret;
417
137k
    int ncid;
418
419
137k
    h5 = grp->nc4_info;
420
137k
    nc = h5->controller;
421
137k
    assert(nc && grp && h5);
422
423
137k
    ncid = nc->ext_ncid | grp->hdr.id;
424
425
    /* Find att, if it exists. (Must check varid first or nc_test will
426
     * break.) This also does lazy att reads if needed. */
427
137k
    if ((ret = getattlist(grp, varid, &var, &attlist)))
428
0
        return ret;
429
430
    /* The length needs to be positive (cast needed for braindead
431
       systems with signed size_t). */
432
137k
    if((unsigned long) len > X_INT_MAX)
433
0
        return NC_EINVAL;
434
435
    /* Check name before LOG statement. */
436
137k
    if (!name || strlen(name) > NC_MAX_NAME)
437
1.39k
        return NC_EBADNAME;
438
439
135k
    LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d",
440
135k
         __func__,ncid, varid, name, file_type, mem_type, len));
441
442
    /* If len is not zero, then there must be some data. */
443
135k
    if (len && !data)
444
0
        return NC_EINVAL;
445
446
    /* If the file is read-only, return an error. */
447
135k
    if (h5->no_write)
448
0
        return NC_EPERM;
449
450
    /* Check and normalize the name. */
451
135k
    if ((retval = nc4_check_name(name, norm_name)))
452
60.7k
        return retval;
453
454
    /* Check that a reserved att name is not being used improperly */
455
75.0k
    const NC_reservedatt* ra = NC_findreserved(name);
456
75.0k
    if(ra != NULL && !force) {
457
        /* case 1: grp=root, varid==NC_GLOBAL, flags & READONLYFLAG */
458
10.3k
        if (nc->ext_ncid == ncid && varid == NC_GLOBAL && grp->parent == NULL
459
478
            && (ra->flags & READONLYFLAG))
460
478
            return NC_ENAMEINUSE;
461
        /* case 2: grp=NA, varid!=NC_GLOBAL, flags & DIMSCALEFLAG */
462
9.84k
        if (varid != NC_GLOBAL && (ra->flags & DIMSCALEFLAG))
463
9.84k
            return NC_ENAMEINUSE;
464
9.84k
    }
465
466
    /* See if there is already an attribute with this name. */
467
64.7k
    att = (NC_ATT_INFO_T*)ncindexlookup(attlist,norm_name);
468
469
64.7k
    if (!att)
470
64.7k
    {
471
        /* If this is a new att, require define mode. */
472
64.7k
        if (!(h5->flags & NC_INDEF))
473
0
        {
474
0
            if (h5->cmode & NC_CLASSIC_MODEL)
475
0
                return NC_ENOTINDEFINE;
476
0
            if ((retval = NC4_redef(ncid)))
477
0
                BAIL(retval);
478
0
        }
479
64.7k
        new_att = NC_TRUE;
480
64.7k
    }
481
0
    else
482
0
    {
483
        /* For an existing att, if we're not in define mode, the len
484
           must not be greater than the existing len for classic model. */
485
0
        if (!(h5->flags & NC_INDEF) &&
486
0
            len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid))
487
0
        {
488
0
            if (h5->cmode & NC_CLASSIC_MODEL)
489
0
                return NC_ENOTINDEFINE;
490
0
            if ((retval = NC4_redef(ncid)))
491
0
                BAIL(retval);
492
0
        }
493
0
    }
494
495
    /* We must have two valid types to continue. */
496
64.7k
    if (file_type == NC_NAT || mem_type == NC_NAT)
497
0
        return NC_EBADTYPE;
498
499
    /* Get information about this type. */
500
64.7k
    if ((retval = nc4_get_typelen_mem(h5, file_type, &type_size)))
501
0
        return retval;
502
503
    /* No character conversions are allowed. */
504
64.7k
    if (file_type != mem_type &&
505
0
        (file_type == NC_CHAR || mem_type == NC_CHAR ||
506
0
         file_type == NC_STRING || mem_type == NC_STRING))
507
0
        return NC_ECHAR;
508
509
    /* For classic mode file, only allow atts with classic types to be
510
     * created. */
511
64.7k
    if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE)
512
0
        return NC_ESTRICTNC3;
513
514
    /* Add to the end of the attribute list, if this att doesn't
515
       already exist. */
516
64.7k
    if (new_att)
517
64.7k
    {
518
64.7k
        LOG((3, "adding attribute %s to the list...", norm_name));
519
64.7k
        if ((ret = nc4_att_list_add(attlist, norm_name, &att)))
520
0
            BAIL(ret);
521
522
        /* Allocate storage for the HDF5 specific att info. */
523
64.7k
        if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T))))
524
0
            BAIL(NC_ENOMEM);
525
64.7k
    }
526
527
    /* Now fill in the metadata. */
528
64.7k
    att->dirty = NC_TRUE;
529
64.7k
    att->nc_typeid = file_type;
530
531
    /* If this att has vlen or string data, release it before we lose the length value. */
532
64.7k
    if (att->stdata)
533
0
    {
534
0
        for (i = 0; i < att->len; i++)
535
0
            if(att->stdata[i])
536
0
                free(att->stdata[i]);
537
0
        free(att->stdata);
538
0
        att->stdata = NULL;
539
0
    }
540
64.7k
    if (att->vldata)
541
0
    {
542
0
        for (i = 0; i < att->len; i++)
543
0
            nc_free_vlen(&att->vldata[i]); /* FIX: see warning of nc_free_vlen */
544
0
        free(att->vldata);
545
0
        att->vldata = NULL;
546
0
    }
547
548
64.7k
    att->len = len;
549
550
    /* If this is the _FillValue attribute, then we will also have to
551
     * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T
552
     * struct for this var. (But ignore a global _FillValue
553
     * attribute). */
554
64.7k
    if (!strcmp(att->hdr.name, _FillValue) && varid != NC_GLOBAL)
555
153
    {
556
153
        int size;
557
558
        /* Fill value must be same type and have exactly one value */
559
153
        if (att->nc_typeid != var->type_info->hdr.id)
560
0
            return NC_EBADTYPE;
561
153
        if (att->len != 1)
562
0
            return NC_EINVAL;
563
564
        /* If we already wrote to the dataset, then return an error. */
565
153
        if (var->written_to)
566
0
            return NC_ELATEFILL;
567
568
        /* Get the length of the veriable data type. */
569
153
        if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id,
570
153
                                          &type_size)))
571
0
            return retval;
572
573
        /* Already set a fill value? Now I'll have to free the old
574
         * one. Make up your damn mind, would you? */
575
153
        if (var->fill_value)
576
0
        {
577
0
            if (var->type_info->nc_type_class == NC_VLEN)
578
0
            {
579
0
                if ((retval = nc_free_vlen(var->fill_value)))
580
0
                    return retval;
581
0
            }
582
0
            else if (var->type_info->nc_type_class == NC_STRING)
583
0
            {
584
0
                if (*(char **)var->fill_value)
585
0
                    free(*(char **)var->fill_value);
586
0
            }
587
0
            free(var->fill_value);
588
0
        }
589
590
        /* Determine the size of the fill value in bytes. */
591
153
        if (var->type_info->nc_type_class == NC_VLEN)
592
0
            size = sizeof(hvl_t);
593
153
        else if (var->type_info->nc_type_class == NC_STRING)
594
0
            size = sizeof(char *);
595
153
        else
596
153
            size = type_size;
597
598
        /* Allocate space for the fill value. */
599
153
        if (!(var->fill_value = calloc(1, size)))
600
0
            return NC_ENOMEM;
601
602
        /* Copy the fill_value. */
603
153
        LOG((4, "Copying fill value into metadata for variable %s", var->hdr.name));
604
153
        if (var->type_info->nc_type_class == NC_VLEN)
605
0
        {
606
0
            nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value);
607
0
            NC_TYPE_INFO_T* basetype;
608
0
            size_t basetypesize = 0;
609
610
            /* get the basetype and its size */
611
0
            basetype = var->type_info;
612
0
            if ((retval = nc4_get_typelen_mem(grp->nc4_info, basetype->hdr.id, &basetypesize)))
613
0
                return retval;
614
            /* shallow clone the content of the vlen; shallow because it has only a temporary existence */
615
0
            fv_vlen->len = in_vlen->len;
616
0
            if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len)))
617
0
                return NC_ENOMEM;
618
0
            memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize);
619
0
        }
620
153
        else if (var->type_info->nc_type_class == NC_STRING)
621
0
        {
622
0
            if (*(char **)data)
623
0
            {
624
0
                if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1)))
625
0
                    return NC_ENOMEM;
626
0
                strcpy(*(char **)var->fill_value, *(char **)data);
627
0
            }
628
0
            else
629
0
                *(char **)var->fill_value = NULL;
630
0
        }
631
153
        else
632
153
            memcpy(var->fill_value, data, type_size);
633
634
        /* Indicate that the fill value was changed, if the variable has already
635
         * been created in the file, so the dataset gets deleted and re-created. */
636
153
        if (var->created)
637
0
            var->fill_val_changed = NC_TRUE;
638
153
    }
639
640
    /* Copy the attribute data, if there is any. VLENs and string
641
     * arrays have to be handled specially. */
642
64.7k
    if (att->len)
643
54.7k
    {
644
54.7k
        nc_type type_class;    /* Class of attribute's type */
645
646
        /* Get class for this type. */
647
54.7k
        if ((retval = nc4_get_typeclass(h5, file_type, &type_class)))
648
0
            return retval;
649
650
54.7k
        assert(data);
651
54.7k
        if (type_class == NC_VLEN)
652
0
        {
653
0
            const hvl_t *vldata1;
654
0
            NC_TYPE_INFO_T *vltype;
655
0
            size_t base_typelen;
656
657
            /* Get the type object for the attribute's type */
658
0
            if ((retval = nc4_find_type(h5, file_type, &vltype)))
659
0
                BAIL(retval);
660
661
            /* Retrieve the size of the base type */
662
0
            if ((retval = nc4_get_typelen_mem(h5, vltype->u.v.base_nc_typeid, &base_typelen)))
663
0
                BAIL(retval);
664
665
0
            vldata1 = data;
666
0
            if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t))))
667
0
                BAIL(NC_ENOMEM);
668
0
            for (i = 0; i < att->len; i++)
669
0
            {
670
0
                att->vldata[i].len = vldata1[i].len;
671
                /* Warning, this only works for cases described for nc_free_vlen() */
672
0
                if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len)))
673
0
                    BAIL(NC_ENOMEM);
674
0
                memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len);
675
0
            }
676
0
        }
677
54.7k
        else if (type_class == NC_STRING)
678
0
        {
679
0
            LOG((4, "copying array of NC_STRING"));
680
0
            if (!(att->stdata = malloc(sizeof(char *) * att->len))) {
681
0
                BAIL(NC_ENOMEM);
682
0
            }
683
684
            /* If we are overwriting an existing attribute,
685
               specifically an NC_CHAR, we need to clean up
686
               the pre-existing att->data. */
687
0
            if (!new_att && att->data) {
688
0
                free(att->data);
689
0
                att->data = NULL;
690
0
            }
691
692
0
            for (i = 0; i < att->len; i++)
693
0
            {
694
0
                if(NULL != ((char **)data)[i]) {
695
0
                    LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1));
696
0
                    if (!(att->stdata[i] = strdup(((char **)data)[i])))
697
0
                        BAIL(NC_ENOMEM);
698
0
                }
699
0
                else
700
0
                    att->stdata[i] = ((char **)data)[i];
701
0
            }
702
0
        }
703
54.7k
        else
704
54.7k
        {
705
            /* [Re]allocate memory for the attribute data */
706
54.7k
            if (!new_att)
707
0
                free (att->data);
708
54.7k
            if (!(att->data = malloc(att->len * type_size)))
709
0
                BAIL(NC_ENOMEM);
710
711
            /* Just copy the data, for non-atomic types */
712
54.7k
            if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM)
713
0
                memcpy(att->data, data, len * type_size);
714
54.7k
            else
715
54.7k
            {
716
                /* Data types are like religions, in that one can convert.  */
717
54.7k
                if ((retval = nc4_convert_type(data, att->data, mem_type, file_type,
718
54.7k
                                               len, &range_error, NULL,
719
54.7k
                                               (h5->cmode & NC_CLASSIC_MODEL))))
720
0
                    BAIL(retval);
721
54.7k
            }
722
54.7k
        }
723
54.7k
    }
724
64.7k
    att->dirty = NC_TRUE;
725
64.7k
    att->created = NC_FALSE;
726
727
    /* Mark attributes on variable dirty, so they get written */
728
64.7k
    if(var)
729
13.1k
        var->attr_dirty = NC_TRUE;
730
731
64.7k
exit:
732
    /* If there was an error return it, otherwise return any potential
733
       range error value. If none, return NC_NOERR as usual.*/
734
64.7k
    if (retval)
735
0
        return retval;
736
64.7k
    if (range_error)
737
0
        return NC_ERANGE;
738
64.7k
    return NC_NOERR;
739
64.7k
}
740
741
/**
742
 * @internal Write an attribute to a netCDF-4/HDF5 file, converting
743
 * data type if necessary.
744
 *
745
 * @param ncid File and group ID.
746
 * @param varid Variable ID.
747
 * @param name Name of attribute.
748
 * @param file_type Type of the attribute data in file.
749
 * @param len Number of elements in attribute array.
750
 * @param data Attribute data.
751
 * @param mem_type Type of data in memory.
752
 *
753
 * @return ::NC_NOERR No error.
754
 * @return ::NC_EINVAL Invalid parameters.
755
 * @return ::NC_EBADID Bad ncid.
756
 * @return ::NC_ENOTVAR Variable not found.
757
 * @return ::NC_EBADNAME Name contains illegal characters.
758
 * @return ::NC_ENAMEINUSE Name already in use.
759
 * @author Ed Hartnett, Dennis Heimbigner
760
 */
761
int
762
NC4_HDF5_put_att(int ncid, int varid, const char *name, nc_type file_type,
763
                 size_t len, const void *data, nc_type mem_type)
764
137k
{
765
137k
    NC_FILE_INFO_T *h5;
766
137k
    NC_GRP_INFO_T *grp;
767
137k
    int ret;
768
769
    /* Find info for this file, group, and h5 info. */
770
137k
    if ((ret = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5)))
771
0
        return ret;
772
137k
    assert(grp && h5);
773
774
137k
    return nc4_put_att(grp, varid, name, file_type, len, data, mem_type, 0);
775
137k
}
776
777
/**
778
 * @internal Learn about an att. All the nc4 nc_inq_ functions just
779
 * call nc4_get_att to get the metadata on an attribute.
780
 *
781
 * @param ncid File and group ID.
782
 * @param varid Variable ID.
783
 * @param name Name of attribute.
784
 * @param xtypep Pointer that gets type of attribute.
785
 * @param lenp Pointer that gets length of attribute data array.
786
 *
787
 * @return ::NC_NOERR No error.
788
 * @return ::NC_EBADID Bad ncid.
789
 * @author Ed Hartnett
790
 */
791
int
792
NC4_HDF5_inq_att(int ncid, int varid, const char *name, nc_type *xtypep,
793
                 size_t *lenp)
794
704k
{
795
704k
    NC_FILE_INFO_T *h5;
796
704k
    NC_GRP_INFO_T *grp;
797
704k
    NC_VAR_INFO_T *var = NULL;
798
704k
    char norm_name[NC_MAX_NAME + 1];
799
704k
    int retval;
800
801
704k
    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
802
803
    /* Find the file, group, and var info, and do lazy att read if
804
     * needed. */
805
704k
    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
806
704k
                                            &h5, &grp, &var, NULL)))
807
0
        return retval;
808
809
    /* If this is one of the reserved atts, use nc_get_att_special. */
810
704k
    if (!var)
811
92.5k
    {
812
92.5k
        const NC_reservedatt *ra = NC_findreserved(norm_name);
813
92.5k
        if (ra  && ra->flags & NAMEONLYFLAG)
814
0
            return nc4_get_att_special(h5, norm_name, xtypep, NC_NAT, lenp, NULL,
815
0
                                       NULL);
816
92.5k
    }
817
818
704k
    return nc4_get_att_ptrs(h5, grp, var, norm_name, xtypep, NC_NAT,
819
704k
                            lenp, NULL, NULL);
820
704k
}
821
822
/**
823
 * @internal Learn an attnum, given a name.
824
 *
825
 * @param ncid File and group ID.
826
 * @param varid Variable ID.
827
 * @param name Name of attribute.
828
 * @param attnump Pointer that gets the attribute index number.
829
 *
830
 * @return ::NC_NOERR No error.
831
 * @author Ed Hartnett
832
 */
833
int
834
NC4_HDF5_inq_attid(int ncid, int varid, const char *name, int *attnump)
835
131k
{
836
131k
    NC_FILE_INFO_T *h5;
837
131k
    NC_GRP_INFO_T *grp;
838
131k
    NC_VAR_INFO_T *var = NULL;
839
131k
    char norm_name[NC_MAX_NAME + 1];
840
131k
    int retval;
841
842
131k
    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
843
844
    /* Find the file, group, and var info, and do lazy att read if
845
     * needed. */
846
131k
    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
847
131k
                                            &h5, &grp, &var, NULL)))
848
0
        return retval;
849
850
    /* If this is one of the reserved atts, use nc_get_att_special. */
851
131k
    if (!var)
852
2.33k
    {
853
2.33k
        const NC_reservedatt *ra = NC_findreserved(norm_name);
854
2.33k
        if (ra  && ra->flags & NAMEONLYFLAG)
855
0
            return nc4_get_att_special(h5, norm_name, NULL, NC_NAT, NULL, attnump,
856
0
                                       NULL);
857
2.33k
    }
858
859
131k
    return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, NC_NAT,
860
131k
                            NULL, attnump, NULL);
861
131k
}
862
863
/**
864
 * @internal Given an attnum, find the att's name.
865
 *
866
 * @param ncid File and group ID.
867
 * @param varid Variable ID.
868
 * @param attnum The index number of the attribute.
869
 * @param name Pointer that gets name of attribute.
870
 *
871
 * @return ::NC_NOERR No error.
872
 * @return ::NC_EBADID Bad ncid.
873
 * @author Ed Hartnett
874
 */
875
int
876
NC4_HDF5_inq_attname(int ncid, int varid, int attnum, char *name)
877
71.4k
{
878
71.4k
    NC_ATT_INFO_T *att;
879
71.4k
    int retval;
880
881
71.4k
    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
882
883
    /* Find the file, group, and var info, and do lazy att read if
884
     * needed. */
885
71.4k
    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, NULL, attnum, 0, NULL,
886
71.4k
                                            NULL, NULL, NULL, &att)))
887
0
        return retval;
888
71.4k
    assert(att);
889
890
    /* Get the name. */
891
71.4k
    if (name)
892
71.4k
        strcpy(name, att->hdr.name);
893
894
71.4k
    return NC_NOERR;
895
71.4k
}
896
897
/**
898
 * @internal Get an attribute.
899
 *
900
 * @param ncid File and group ID.
901
 * @param varid Variable ID.
902
 * @param name Name of attribute.
903
 * @param value Pointer that gets attribute data.
904
 * @param memtype The type the data should be converted to as it is
905
 * read.
906
 *
907
 * @return ::NC_NOERR No error.
908
 * @return ::NC_EBADID Bad ncid.
909
 * @author Ed Hartnett
910
 */
911
int
912
NC4_HDF5_get_att(int ncid, int varid, const char *name, void *value,
913
                 nc_type memtype)
914
86.6k
{
915
86.6k
    NC_FILE_INFO_T *h5;
916
86.6k
    NC_GRP_INFO_T *grp;
917
86.6k
    NC_VAR_INFO_T *var = NULL;
918
86.6k
    char norm_name[NC_MAX_NAME + 1];
919
86.6k
    int retval;
920
921
86.6k
    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
922
923
    /* Find the file, group, and var info, and do lazy att read if
924
     * needed. */
925
86.6k
    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
926
86.6k
                                            &h5, &grp, &var, NULL)))
927
0
        return retval;
928
929
    /* If this is one of the reserved atts, use nc_get_att_special. */
930
86.6k
    if (!var)
931
55.6k
    {
932
55.6k
        const NC_reservedatt *ra = NC_findreserved(norm_name);
933
55.6k
        if (ra  && ra->flags & NAMEONLYFLAG)
934
0
            return nc4_get_att_special(h5, norm_name, NULL, NC_NAT, NULL, NULL,
935
0
                                       value);
936
55.6k
    }
937
938
86.6k
    return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, memtype,
939
86.6k
                            NULL, NULL, value);
940
86.6k
}