Coverage Report

Created: 2023-05-28 06:42

/src/netcdf-c/libnczarr/zmap_file.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright 2018, University Corporation for Atmospheric Research
3
 *      See netcdf/COPYRIGHT file for copying and redistribution conditions.
4
 */
5
6
#undef DEBUG
7
8
/* Not sure this has any effect */
9
#define _LARGEFILE_SOURCE 1
10
#define _LARGEFILE64_SOURCE 1
11
12
#include "zincludes.h"
13
14
#include <errno.h>
15
16
#ifdef HAVE_SYS_TYPES_H
17
#include <sys/types.h>
18
#endif
19
#ifdef HAVE_UNISTD_H
20
#include <unistd.h>
21
#endif
22
#ifdef HAVE_FCNTL_H
23
#include <fcntl.h>
24
#endif
25
#ifdef HAVE_SYS_STAT_H
26
#include <sys/stat.h>
27
#endif
28
#ifdef HAVE_DIRENT_H
29
#include <dirent.h>
30
#endif
31
32
#ifdef _WIN32
33
#include <windows.h>
34
#ifndef S_ISDIR
35
#define S_ISDIR(mode) ((mode) & _S_IFDIR)
36
#define S_ISREG(mode) ((mode) & _S_IFREG)
37
#endif
38
#if 0
39
#ifndef __cplusplus
40
#include <io.h>
41
#include <iostream>
42
#endif
43
#endif
44
#endif
45
46
#include "fbits.h"
47
#include "ncpathmgr.h"
48
49
#define VERIFY
50
51
#ifndef O_DIRECTORY
52
# define O_DIRECTORY  0200000
53
#endif
54
55
/*Mnemonic*/
56
0
#define FLAG_ISDIR 1
57
#define FLAG_CREATE 1
58
0
#define SKIPLAST 1
59
#define WHOLEPATH 0
60
61
#define NCZM_FILE_V1 1
62
63
#ifdef S_IRUSR
64
static int NC_DEFAULT_CREATE_PERMS =
65
           (S_IRUSR|S_IWUSR        |S_IRGRP|S_IWGRP);
66
static int NC_DEFAULT_RWOPEN_PERMS =
67
           (S_IRUSR|S_IWUSR        |S_IRGRP|S_IWGRP);
68
static int NC_DEFAULT_ROPEN_PERMS =
69
//           (S_IRUSR                |S_IRGRP);
70
           (S_IRUSR|S_IWUSR        |S_IRGRP|S_IWGRP);
71
static int NC_DEFAULT_DIR_PERMS =
72
       (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IWGRP);
73
#else
74
static int NC_DEFAULT_CREATE_PERMS = 0660;
75
static int NC_DEFAULT_RWOPEN_PERMS = 0660;
76
static int NC_DEFAULT_ROPEN_PERMS = 0660;
77
static int NC_DEFAULT_DIR_PERMS = 0770;
78
#endif
79
80
/*
81
Do a simple mapping of our simplified map model
82
to a file system.
83
84
Every dataset is assumed to be rooted at some directory in the
85
file tree. So, its location is defined by some path to a
86
directory representing both the dataset and the root group of
87
that dataset. The root is recognized because it uniquely
88
contains a "superblock" file name ".nczarr" that provides
89
general information about a dataset. Nesting a dataset
90
inside a dataset is prohibited. This can be detected
91
by looking for an occurrence of a ".nczarr" file in any containing
92
directory. If such a file is found, then an illegal nested
93
dataset has been found.
94
95
For the object API, the mapping is as follows:
96
1. Every content-bearing object (e.g. .zgroup or .zarray) is mapped to a file.
97
   The key constraint is that the content bearing objects are files.
98
   This means that if a key  points to a content bearing object then
99
   no other key can have that content bearing key as a suffix.
100
2. The meta data containing files are assumed to contain
101
   UTF-8 character data.
102
3. The chunk containing files are assumed to contain raw unsigned 8-bit byte data.
103
*/
104
105
/* define the var name containing an objects content */
106
#define ZCONTENT "data"
107
108
typedef struct FD {
109
  int fd;
110
} FD;
111
112
static FD FDNUL = {-1};
113
114
/* Define the "subclass" of NCZMAP */
115
typedef struct ZFMAP {
116
    NCZMAP map;
117
    char* root;
118
} ZFMAP;
119
120
/* Forward */
121
static NCZMAP_API zapi;
122
static int zfileclose(NCZMAP* map, int delete);
123
static int zfcreategroup(ZFMAP*, const char* key, int nskip);
124
static int zflookupobj(ZFMAP*, const char* key, FD* fd);
125
static int zfparseurl(const char* path0, NCURI** urip);
126
static int zffullpath(ZFMAP* zfmap, const char* key, char**);
127
static void zfrelease(ZFMAP* zfmap, FD* fd);
128
static void zfunlink(const char* canonpath);
129
130
static int platformerr(int err);
131
static int platformcreatefile(ZFMAP* map, const char* truepath,FD*);
132
static int platformcreatedir(ZFMAP* map, const char* truepath);
133
static int platformopenfile(ZFMAP* zfmap, const char* truepath, FD* fd);
134
static int platformopendir(ZFMAP* map, const char* truepath);
135
static int platformdircontent(ZFMAP* map, const char* path, NClist* contents);
136
static int platformdelete(ZFMAP* map, const char* path, int delroot);
137
static int platformseek(ZFMAP* map, FD* fd, int pos, size64_t* offset);
138
static int platformread(ZFMAP* map, FD* fd, size64_t count, void* content);
139
static int platformwrite(ZFMAP* map, FD* fd, size64_t count, const void* content);
140
static void platformrelease(ZFMAP* zfmap, FD* fd);
141
static int platformtestcontentbearing(ZFMAP* zfmap, const char* truepath);
142
143
#ifdef VERIFY
144
static int verify(const char* path, int isdir);
145
static int verifykey(const char* key, int isdir);
146
#endif
147
148
static int zfinitialized = 0;
149
static void
150
zfileinitialize(void)
151
0
{
152
0
    if(!zfinitialized) {
153
0
        ZTRACE(5,NULL);
154
0
  const char* env = NULL;
155
0
  int perms = 0;
156
0
  env = getenv("NC_DEFAULT_CREATE_PERMS");
157
0
  if(env != NULL && strlen(env) > 0) {
158
0
      if(sscanf(env,"%d",&perms) == 1) NC_DEFAULT_CREATE_PERMS = perms;
159
0
  }
160
0
  env = getenv("NC_DEFAULT_DIR_PERMS");
161
0
  if(env != NULL && strlen(env) > 0) {
162
0
      if(sscanf(env,"%d",&perms) == 1) NC_DEFAULT_DIR_PERMS = perms;
163
0
  }
164
0
        zfinitialized = 1;
165
0
  (void)ZUNTRACE(NC_NOERR);
166
0
    }
167
0
}
168
169
/* Define the Dataset level API */
170
171
/*
172
@param datasetpath abs path in the file tree of the root of the dataset'
173
       might be a relative path.
174
@param mode the netcdf-c mode flags
175
@param flags extra flags
176
@param flags extra parameters
177
@param mapp return the map object in this
178
*/
179
180
static int
181
zfilecreate(const char *path, int mode, size64_t flags, void* parameters, NCZMAP** mapp)
182
0
{
183
0
    int stat = NC_NOERR;
184
0
    char* canonpath = NULL;
185
0
    char* abspath = NULL;
186
0
    ZFMAP* zfmap = NULL;
187
0
    NCURI* url = NULL;
188
  
189
0
    NC_UNUSED(parameters);
190
0
    ZTRACE(5,"path=%s mode=%d flag=%llu",path,mode,flags);
191
192
0
    if(!zfinitialized) zfileinitialize();
193
194
    /* Fixup mode flags */
195
0
    mode |= (NC_NETCDF4 | NC_WRITE);
196
197
0
    if(!(mode & NC_WRITE))
198
0
        {stat = NC_EPERM; goto done;}
199
200
    /* path must be a url with file: protocol*/
201
0
    if((stat=zfparseurl(path,&url)))
202
0
  goto done;
203
0
    if(strcasecmp(url->protocol,"file") != 0)
204
0
        {stat = NC_EURL; goto done;}
205
206
    /* Convert the root path */
207
0
    if((canonpath = NCpathcvt(url->path))==NULL)
208
0
  {stat = NC_ENOMEM; goto done;}
209
210
    /* Make the root path be absolute */
211
0
    if((abspath = NCpathabsolute(canonpath)) == NULL)
212
0
  {stat = NC_EURL; goto done;}
213
214
    /* Build the zmap state */
215
0
    if((zfmap = calloc(1,sizeof(ZFMAP))) == NULL)
216
0
  {stat = NC_ENOMEM; goto done;}
217
218
0
    zfmap->map.format = NCZM_FILE;
219
0
    zfmap->map.url = ncuribuild(url,NULL,NULL,NCURIALL);
220
0
    zfmap->map.flags = flags;
221
    /* create => NC_WRITE */
222
0
    zfmap->map.mode = mode;
223
0
    zfmap->map.api = &zapi;
224
0
    zfmap->root = abspath;
225
0
        abspath = NULL;
226
227
    /* If NC_CLOBBER, then delete below file tree */
228
0
    if(!fIsSet(mode,NC_NOCLOBBER))
229
0
  platformdelete(zfmap,zfmap->root,0);
230
    
231
    /* make sure we can access the root directory; create if necessary */
232
0
    if((stat = platformcreatedir(zfmap, zfmap->root)))
233
0
  goto done;
234
235
    /* Dataset superblock will be written by higher layer */
236
     
237
0
    if(mapp) *mapp = (NCZMAP*)zfmap;    
238
239
0
done:
240
0
    ncurifree(url);
241
0
    nullfree(canonpath);
242
0
    nullfree(abspath);
243
0
    if(stat)
244
0
      zfileclose((NCZMAP*)zfmap,1);
245
0
    return ZUNTRACE(stat);
246
0
}
247
248
/*
249
@param datasetpath abs path in the file tree of the root of the dataset'
250
       might be a relative path.
251
@param mode the netcdf-c mode flags
252
@param flags extra flags
253
@param flags extra parameters
254
@param mapp return the map object in this
255
*/
256
257
static int
258
zfileopen(const char *path, int mode, size64_t flags, void* parameters, NCZMAP** mapp)
259
0
{
260
0
    int stat = NC_NOERR;
261
0
    char* canonpath = NULL;
262
0
    char* abspath = NULL;
263
0
    ZFMAP* zfmap = NULL;
264
0
    NCURI*url = NULL;
265
    
266
0
    NC_UNUSED(parameters);
267
0
    ZTRACE(5,"path=%s mode=%d flags=%llu",path,mode,flags);
268
269
0
    if(!zfinitialized) zfileinitialize();
270
271
    /* Fixup mode flags */
272
0
    mode = (NC_NETCDF4 | mode);
273
274
    /* path must be a url with file: protocol*/
275
0
    if((stat=zfparseurl(path,&url)))
276
0
  goto done;
277
0
    if(strcasecmp(url->protocol,"file") != 0)
278
0
        {stat = NC_EURL; goto done;}
279
280
    /* Convert the root path */
281
0
    if((canonpath = NCpathcvt(url->path))==NULL)
282
0
  {stat = NC_ENOMEM; goto done;}
283
284
    /* Make the root path be absolute */
285
0
    if((abspath = NCpathabsolute(canonpath)) == NULL)
286
0
  {stat = NC_EURL; goto done;}
287
288
    /* Build the zmap state */
289
0
    if((zfmap = calloc(1,sizeof(ZFMAP))) == NULL)
290
0
  {stat = NC_ENOMEM; goto done;}
291
292
0
    zfmap->map.format = NCZM_FILE;
293
0
    zfmap->map.url = ncuribuild(url,NULL,NULL,NCURIALL);
294
0
    zfmap->map.flags = flags;
295
0
    zfmap->map.mode = mode;
296
0
    zfmap->map.api = (NCZMAP_API*)&zapi;
297
0
    zfmap->root = abspath;
298
0
  abspath = NULL;
299
    
300
    /* Verify root dir exists */
301
0
    if((stat = platformopendir(zfmap,zfmap->root)))
302
0
  goto done;
303
    
304
    /* Dataset superblock will be read by higher layer */
305
    
306
0
    if(mapp) *mapp = (NCZMAP*)zfmap;    
307
308
0
done:
309
0
    ncurifree(url);
310
0
    nullfree(canonpath);
311
0
    nullfree(abspath);
312
0
    if(stat) zfileclose((NCZMAP*)zfmap,0);
313
0
    return ZUNTRACE(stat);
314
0
}
315
316
/**************************************************/
317
/* Object API */
318
319
static int
320
zfileexists(NCZMAP* map, const char* key)
321
0
{
322
0
    int stat = NC_NOERR;
323
0
    ZFMAP* zfmap = (ZFMAP*)map;
324
0
    FD fd = FDNUL;
325
326
0
    ZTRACE(5,"map=%s key=%s",zfmap->map.url,key);
327
0
    switch(stat=zflookupobj(zfmap,key,&fd)) {
328
0
    case NC_NOERR: break;
329
0
    case NC_ENOOBJECT: stat = NC_EEMPTY;
330
0
    case NC_EEMPTY: break;
331
0
    default: break;
332
0
    }
333
0
    zfrelease(zfmap,&fd);    
334
0
    return ZUNTRACE(stat);
335
0
}
336
337
static int
338
zfilelen(NCZMAP* map, const char* key, size64_t* lenp)
339
0
{
340
0
    int stat = NC_NOERR;
341
0
    ZFMAP* zfmap = (ZFMAP*)map;
342
0
    size64_t len = 0;
343
0
    FD fd = FDNUL;
344
345
0
    ZTRACE(5,"map=%s key=%s",map->url,key);
346
347
0
    switch (stat=zflookupobj(zfmap,key,&fd)) {
348
0
    case NC_NOERR:
349
        /* Get file size */
350
0
        if((stat=platformseek(zfmap, &fd, SEEK_END, &len))) goto done;
351
0
  break;
352
0
    case NC_ENOOBJECT: stat = NC_EEMPTY;
353
0
    case NC_EEMPTY: break;
354
0
    default: break;
355
0
    }
356
0
    zfrelease(zfmap,&fd);
357
0
    if(lenp) *lenp = len;
358
359
0
done:
360
0
    return ZUNTRACEX(stat,"len=%llu",(lenp?*lenp:777777777777));
361
0
}
362
363
static int
364
zfileread(NCZMAP* map, const char* key, size64_t start, size64_t count, void* content)
365
0
{
366
0
    int stat = NC_NOERR;
367
0
    FD fd = FDNUL;
368
0
    ZFMAP* zfmap = (ZFMAP*)map; /* cast to true type */
369
    
370
0
    ZTRACE(5,"map=%s key=%s start=%llu count=%llu",map->url,key,start,count);
371
372
0
#ifdef VERIFY
373
0
    if(!verifykey(key,!FLAG_ISDIR))
374
0
        assert(!"expected file, have dir");
375
0
#endif
376
377
0
    switch (stat = zflookupobj(zfmap,key,&fd)) {
378
0
    case NC_NOERR:
379
0
        if((stat = platformseek(zfmap, &fd, SEEK_SET, &start))) goto done;
380
0
        if((stat = platformread(zfmap, &fd, count, content))) goto done;
381
0
  break;
382
0
    case NC_ENOOBJECT: stat = NC_EEMPTY;
383
0
    case NC_EEMPTY: break;
384
0
    default: break;
385
0
    }
386
    
387
0
done:
388
0
    zfrelease(zfmap,&fd);
389
0
    return ZUNTRACE(stat);
390
0
}
391
392
static int
393
zfilewrite(NCZMAP* map, const char* key, size64_t start, size64_t count, const void* content)
394
0
{
395
0
    int stat = NC_NOERR;
396
0
    FD fd = FDNUL;
397
0
    ZFMAP* zfmap = (ZFMAP*)map; /* cast to true type */
398
0
    char* truepath = NULL;
399
400
0
    ZTRACE(5,"map=%s key=%s start=%llu count=%llu",map->url,key,start,count);
401
402
0
#ifdef VERIFY
403
0
    if(!verifykey(key,!FLAG_ISDIR))
404
0
        assert(!"expected file, have dir");
405
0
#endif
406
407
0
    switch (stat = zflookupobj(zfmap,key,&fd)) {
408
0
    case NC_ENOOBJECT:
409
0
    case NC_EEMPTY:
410
0
  stat = NC_NOERR;
411
  /* Create the directories leading to this */
412
0
  if((stat = zfcreategroup(zfmap,key,SKIPLAST))) goto done;
413
        /* Create truepath */
414
0
        if((stat = zffullpath(zfmap,key,&truepath))) goto done;
415
  /* Create file */
416
0
  if((stat = platformcreatefile(zfmap,truepath,&fd))) goto done;
417
  /* Fall thru to write the object */
418
0
    case NC_NOERR:
419
0
        if((stat = platformseek(zfmap, &fd, SEEK_SET, &start))) goto done;
420
0
        if((stat = platformwrite(zfmap, &fd, count, content))) goto done;
421
0
  break;
422
0
    default: break;
423
0
    }
424
425
0
done:
426
0
    nullfree(truepath);
427
0
    zfrelease(zfmap,&fd);
428
0
    return ZUNTRACE(stat);
429
0
}
430
431
static int
432
zfileclose(NCZMAP* map, int delete)
433
0
{
434
0
    int stat = NC_NOERR;
435
0
    ZFMAP* zfmap = (ZFMAP*)map;
436
437
0
    ZTRACE(5,"map=%s delete=%d",map->url,delete);
438
0
    if(zfmap == NULL) return NC_NOERR;
439
    
440
    /* Delete the subtree below the root and the root */
441
0
    if(delete) {
442
0
  stat = platformdelete(zfmap,zfmap->root,1);
443
0
  zfunlink(zfmap->root);
444
0
    }
445
0
    nczm_clear(map);
446
0
    nullfree(zfmap->root);
447
0
    zfmap->root = NULL;
448
0
    free(zfmap);
449
0
    return ZUNTRACE(stat);
450
0
}
451
452
/*
453
Return a list of names immediately "below" a specified prefix key.
454
In theory, the returned list should be sorted in lexical order,
455
but it possible that it is not.
456
The prefix key is not included. 
457
*/
458
int
459
zfilesearch(NCZMAP* map, const char* prefixkey, NClist* matches)
460
0
{
461
0
    int stat = NC_NOERR;
462
0
    ZFMAP* zfmap = (ZFMAP*)map;
463
0
    char* fullpath = NULL;
464
0
    NClist* nextlevel = nclistnew();
465
0
    NCbytes* buf = ncbytesnew();
466
467
0
    ZTRACE(5,"map=%s prefixkey=%s",map->url,prefixkey);
468
469
    /* Make the root path be true */
470
0
    if(prefixkey == NULL || strlen(prefixkey)==0 || strcmp(prefixkey,"/")==0)
471
0
  fullpath = strdup(zfmap->root);
472
0
    else if((stat = nczm_concat(zfmap->root,prefixkey,&fullpath))) goto done;
473
474
    /* get names of the next level path entries */
475
0
    switch (stat = platformdircontent(zfmap, fullpath, nextlevel)) {
476
0
    case NC_NOERR: /* ok */
477
0
  break;
478
0
    case NC_EEMPTY: /* not a dir */
479
0
  stat = NC_NOERR;
480
0
  goto done;
481
0
    case NC_ENOOBJECT:
482
0
    default:
483
0
  goto done;
484
0
    }
485
0
    while(nclistlength(nextlevel) > 0) {
486
0
  char* segment = nclistremove(nextlevel,0);
487
0
  nclistpush(matches,segment);
488
0
    }
489
    
490
0
done:
491
0
    nclistfreeall(nextlevel);
492
0
    ncbytesfree(buf);
493
0
    nullfree(fullpath);
494
0
    return ZUNTRACEX(stat,"|matches|=%d",(int)nclistlength(matches));
495
0
}
496
497
/**************************************************/
498
/* Utilities */
499
500
static void
501
zfunlink(const char* canonpath)
502
0
{
503
0
    char* local = NULL;
504
0
    if((local = NCpathcvt(canonpath))==NULL) goto done;
505
0
    unlink(local);
506
0
done:
507
0
    nullfree(local);
508
0
}
509
510
/* Lookup a group by parsed path (segments)*/
511
/* Return NC_EEMPTY if not found, NC_EINVAL if not a directory; create if create flag is set */
512
static int
513
zfcreategroup(ZFMAP* zfmap, const char* key, int nskip)
514
0
{
515
0
    int stat = NC_NOERR;
516
0
    int i, len;
517
0
    char* fullpath = NULL;
518
0
    NCbytes* path = ncbytesnew();
519
0
    NClist* segments = nclistnew();
520
521
0
    ZTRACE(5,"map=%s key=%s nskip=%d",zfmap->map.url,key,nskip);
522
0
    if((stat=nczm_split(key,segments)))
523
0
  goto done;    
524
0
    len = nclistlength(segments);
525
0
    len -= nskip; /* leave off last nskip segments */
526
0
    ncbytescat(path,zfmap->root); /* We need path to be absolute */
527
0
    for(i=0;i<len;i++) {
528
0
  const char* seg = nclistget(segments,i);
529
0
  ncbytescat(path,"/");
530
0
  ncbytescat(path,seg);
531
  /* open and optionally create the directory */  
532
0
  stat = platformcreatedir(zfmap,ncbytescontents(path));
533
0
  if(stat) goto done;
534
0
    }
535
0
done:
536
0
    nullfree(fullpath);
537
0
    ncbytesfree(path);
538
0
    nclistfreeall(segments);
539
0
    return ZUNTRACE(stat);
540
0
}
541
542
/* Lookup an object
543
@return NC_NOERR if found and is a content-bearing object
544
@return NC_EEMPTY if exists but is not-content-bearing
545
@return NC_ENOOBJECT if not found
546
*/
547
static int
548
zflookupobj(ZFMAP* zfmap, const char* key, FD* fd)
549
0
{
550
0
    int stat = NC_NOERR;
551
0
    char* path = NULL;
552
553
0
    ZTRACE(5,"map=%s key=%s",zfmap->map.url,key);
554
555
0
    if((stat = zffullpath(zfmap,key,&path)))
556
0
  {goto done;}    
557
558
    /* See if this is content-bearing */
559
0
    if((stat = platformtestcontentbearing(zfmap,path)))
560
0
  goto done;        
561
562
    /* Open the file */
563
0
    if((stat = platformopenfile(zfmap,path,fd)))
564
0
        goto done;
565
566
0
done:
567
0
    errno = 0;
568
0
    nullfree(path);
569
0
    return ZUNTRACE(stat);
570
0
}
571
572
/* When we are finished accessing object */
573
static void
574
zfrelease(ZFMAP* zfmap, FD* fd)
575
0
{
576
0
    ZTRACE(5,"map=%s fd=%d",zfmap->map.url,(fd?fd->fd:-1));
577
0
    platformrelease(zfmap,fd);
578
0
    (void)ZUNTRACE(NC_NOERR);
579
0
}
580
581
/**************************************************/
582
/* External API objects */
583
584
NCZMAP_DS_API zmap_file = {
585
    NCZM_FILE_V1,
586
    0,
587
    zfilecreate,
588
    zfileopen,
589
};
590
591
static NCZMAP_API zapi = {
592
    NCZM_FILE_V1,
593
    zfileclose,
594
    zfileexists,
595
    zfilelen,
596
    zfileread,
597
    zfilewrite,
598
    zfilesearch,
599
};
600
601
static int
602
zffullpath(ZFMAP* zfmap, const char* key, char** pathp)
603
0
{
604
0
    int stat = NC_NOERR;
605
0
    size_t klen, pxlen, flen;
606
0
    char* path = NULL;
607
608
0
    ZTRACE(6,"map=%s key=%s",zfmap->map.url,key);
609
610
0
    klen = nulllen(key);
611
0
    pxlen = strlen(zfmap->root);
612
0
    flen = klen+pxlen+1+1;
613
0
    if((path = malloc(flen)) == NULL) {stat = NC_ENOMEM; goto done;}
614
0
    path[0] = '\0';
615
0
    strlcat(path,zfmap->root,flen);
616
    /* look for special cases */
617
0
    if(key != NULL) {
618
0
        if(key[0] != '/') strlcat(path,"/",flen);
619
0
  if(strcmp(key,"/") != 0)
620
0
            strlcat(path,key,flen);
621
0
    }
622
0
    if(pathp) {*pathp = path; path = NULL;}
623
0
done:
624
0
    nullfree(path)
625
0
    return ZUNTRACEX(stat,"path=%s",(pathp?*pathp:"null"));
626
0
}
627
628
static int
629
zfparseurl(const char* path0, NCURI** urip)
630
0
{
631
0
    int stat = NC_NOERR;
632
0
    NCURI* uri = NULL;
633
0
    ZTRACE(6,"path0=%s",path0);
634
0
    ncuriparse(path0,&uri);
635
0
    if(uri == NULL)
636
0
  {stat = NC_EURL; goto done;}
637
0
    if(urip) {*urip = uri; uri = NULL;}
638
639
0
done:
640
0
    ncurifree(uri);
641
0
    return ZUNTRACEX(stat,"uri=%p",(urip?(void*)*urip:(void*)urip));
642
0
    return stat;
643
0
}
644
645
/**************************************************/
646
static int
647
platformerr(int err)
648
0
{
649
0
    ZTRACE(6,"err=%d",err);
650
0
    switch (err) {
651
0
     case ENOENT: err = NC_ENOOBJECT; break; /* File does not exist */
652
0
     case ENOTDIR: err = NC_EEMPTY; break; /* no content */
653
0
     case EACCES: err = NC_EAUTH; break; /* file permissions */
654
0
     case EPERM:  err = NC_EAUTH; break; /* ditto */
655
0
     default: break;
656
0
     }
657
0
     return ZUNTRACE(err);
658
0
}
659
660
/* Test type of the specified file.
661
@return NC_NOERR if found and is a content-bearing object (file)
662
@return NC_EEMPTY if exists but is not-content-bearing (a directory)
663
@return NC_ENOOBJECT if not found
664
*/
665
static int
666
platformtestcontentbearing(ZFMAP* zfmap, const char* canonpath)
667
0
{
668
0
    int ret = 0;
669
0
    struct stat buf;
670
    
671
0
    ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
672
673
0
    errno = 0;
674
0
    ret = NCstat(canonpath, &buf);
675
0
    ZTRACEMORE(6,"\tstat: ret=%d, errno=%d st_mode=%d",ret,errno,buf.st_mode);
676
0
    if(ret < 0) {
677
0
  ret = platformerr(errno);
678
0
    } else if(S_ISDIR(buf.st_mode)) {
679
0
        ret = NC_EEMPTY;
680
0
    } else
681
0
        ret = NC_NOERR;
682
0
    errno = 0;
683
0
    return ZUNTRACE(ret);
684
0
}
685
686
/* Create a file */
687
static int
688
platformcreatefile(ZFMAP* zfmap, const char* canonpath, FD* fd)
689
0
{
690
0
    int stat = NC_NOERR;
691
0
    int ioflags = 0;
692
0
    int createflags = 0;
693
0
    int mode = zfmap->map.mode;
694
0
    int permissions = NC_DEFAULT_ROPEN_PERMS;
695
696
0
    ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
697
    
698
0
    errno = 0;
699
0
    if(!fIsSet(mode, NC_WRITE))
700
0
        ioflags |= (O_RDONLY);
701
0
    else {
702
0
        ioflags |= (O_RDWR);
703
0
  permissions = NC_DEFAULT_RWOPEN_PERMS;
704
0
    }
705
#ifdef O_BINARY
706
    fSet(ioflags, O_BINARY);
707
#endif
708
709
0
    if(fIsSet(mode, NC_NOCLOBBER))
710
0
        fSet(createflags, O_EXCL);
711
0
    else
712
0
  fSet(createflags, O_TRUNC);
713
714
0
    if(fIsSet(mode,NC_WRITE))
715
0
        createflags = (ioflags|O_CREAT);
716
717
    /* Try to create file (will also NCpathcvt) */
718
0
    fd->fd = NCopen3(canonpath, createflags, permissions);
719
0
    if(fd->fd < 0) { /* could not create */
720
0
  stat = platformerr(errno);
721
0
        goto done; /* could not open */
722
0
    }
723
0
done:
724
0
    errno = 0;
725
0
    return ZUNTRACEX(stat,"fd=%d",(fd?fd->fd:-1));
726
0
}
727
728
/* Open a file; fail if it does not exist */
729
static int
730
platformopenfile(ZFMAP* zfmap, const char* canonpath, FD* fd)
731
0
{
732
0
    int stat = NC_NOERR;
733
0
    int ioflags = 0;
734
0
    int mode = zfmap->map.mode;
735
0
    int permissions = 0;
736
737
0
    ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
738
739
0
    errno = 0;
740
0
    if(!fIsSet(mode, NC_WRITE)) {
741
0
        ioflags |= (O_RDONLY);
742
0
  permissions = NC_DEFAULT_ROPEN_PERMS;
743
0
    } else {
744
0
        ioflags |= (O_RDWR);
745
0
  permissions = NC_DEFAULT_RWOPEN_PERMS;
746
0
    }
747
#ifdef O_BINARY
748
    fSet(ioflags, O_BINARY);
749
#endif
750
751
0
#ifdef VERIFY
752
0
    if(!verify(canonpath,!FLAG_ISDIR))
753
0
        assert(!"expected file, have dir");
754
0
#endif
755
756
    /* Try to open file  (will localize) */
757
0
    fd->fd = NCopen3(canonpath, ioflags, permissions);
758
0
    if(fd->fd < 0)
759
0
        {stat = platformerr(errno); goto done;} /* could not open */
760
0
done:
761
0
    errno = 0;
762
0
    return ZUNTRACEX(stat,"fd=%d",(fd?fd->fd:-1));
763
0
}
764
765
/* Create a dir */
766
static int
767
platformcreatedir(ZFMAP* zfmap, const char* canonpath)
768
0
{
769
0
    int ret = NC_NOERR;
770
0
    int mode = zfmap->map.mode;
771
772
0
    ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
773
774
0
    errno = 0;
775
    /* Try to access file as if it exists */
776
0
    ret = NCaccess(canonpath,ACCESS_MODE_EXISTS);
777
0
    if(ret < 0) { /* it does not exist, then it can be anything */
778
0
  if(fIsSet(mode,NC_WRITE)) {
779
      /* Try to create it */
780
            /* Create the directory using mkdir */
781
0
        if(NCmkdir(canonpath,NC_DEFAULT_DIR_PERMS) < 0)
782
0
          {ret = platformerr(errno); goto done;}
783
      /* try to access again */
784
0
      ret = NCaccess(canonpath,ACCESS_MODE_EXISTS);
785
0
          if(ret < 0)
786
0
          {ret = platformerr(errno); goto done;}
787
0
  } else
788
0
      {ret = platformerr(errno); goto done;}  
789
0
    }
790
791
0
done:
792
0
    errno = 0;
793
0
    return ZUNTRACE(ret);
794
0
}
795
796
/* Open a dir; fail if it does not exist */
797
static int
798
platformopendir(ZFMAP* zfmap, const char* canonpath)
799
0
{
800
0
    int ret = NC_NOERR;
801
802
0
    ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
803
804
0
    errno = 0;
805
    /* Try to access file as if it exists */
806
0
    ret = NCaccess(canonpath,ACCESS_MODE_EXISTS);
807
0
    if(ret < 0)
808
0
  {ret = platformerr(errno); goto done;}  
809
0
done:
810
0
    errno = 0;
811
0
    return ZUNTRACE(ret);
812
0
}
813
814
/**
815
Given a path, return the list of all files+dirs immediately below
816
the specified path: e.g. X s.t. path/X exists.
817
There are several possibilities:
818
1. path does not exist => return NC_ENOTFOUND
819
2. path is not a directory => return NC_EEMPTY and |contents| == 0
820
3. path is a directory => return NC_NOERR and |contents| >= 0
821
822
@return NC_NOERR if path is a directory
823
@return NC_EEMPTY if path is not a directory
824
@return NC_ENOTFOUND if path does not exist
825
*/
826
827
#ifdef _WIN32
828
static int
829
platformdircontent(ZFMAP* zfmap, const char* canonpath, NClist* contents)
830
{
831
    int ret = NC_NOERR;
832
    errno = 0;
833
    WIN32_FIND_DATA FindFileData;
834
    HANDLE dir = NULL;
835
    char* ffpath = NULL;
836
    char* lpath = NULL;
837
    size_t len;
838
    char* d = NULL;
839
840
    ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
841
842
    switch (ret = platformtestcontentbearing(zfmap, canonpath)) {
843
    case NC_EEMPTY: ret = NC_NOERR; break; /* directory */    
844
    case NC_NOERR: ret = NC_EEMPTY; goto done;
845
    default: goto done;
846
    }
847
848
    /* We need to process the path to make it work with FindFirstFile */
849
    len = strlen(canonpath);
850
    /* Need to terminate path with '/''*' */
851
    ffpath = (char*)malloc(len+2+1);
852
    memcpy(ffpath,canonpath,len);
853
    if(canonpath[len-1] != '/') {
854
  ffpath[len] = '/';  
855
  len++;
856
    }
857
    ffpath[len] = '*'; len++;
858
    ffpath[len] = '\0';
859
860
    /* localize it */
861
    if((lpath = NCpathcvt(ffpath))==NULL)
862
  {ret = NC_ENOMEM; goto done;}
863
    dir = FindFirstFile(lpath, &FindFileData);
864
    if(dir == INVALID_HANDLE_VALUE) {
865
  /* Distinquish not-a-directory from no-matching-file */
866
        switch (GetLastError()) {
867
  case ERROR_FILE_NOT_FOUND: /* No matching files */ /* fall thru */
868
      ret = NC_NOERR;
869
      goto done;
870
  case ERROR_DIRECTORY: /* not a directory */
871
  default:
872
            ret = NC_EEMPTY;
873
      goto done;
874
  }
875
    }
876
    do {
877
  const char* name = NULL;
878
        name = FindFileData.cFileName;
879
  if(strcmp(name,".")==0 || strcmp(name,"..")==0)
880
      continue;
881
  nclistpush(contents,strdup(name));
882
    } while(FindNextFile(dir, &FindFileData));
883
884
done:
885
    if(dir) FindClose(dir);
886
    nullfree(lpath);
887
    nullfree(ffpath);
888
    nullfree(d);
889
    errno = 0;
890
    return ZUNTRACEX(ret,"|contents|=%d",(int)nclistlength(contents));
891
}
892
893
#else /*!_WIN32*/
894
895
static int
896
platformdircontent(ZFMAP* zfmap, const char* canonpath, NClist* contents)
897
0
{
898
0
    int ret = NC_NOERR;
899
0
    errno = 0;
900
0
    DIR* dir = NULL;
901
902
0
    ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
903
904
0
    switch (ret = platformtestcontentbearing(zfmap, canonpath)) {
905
0
    case NC_EEMPTY: ret = NC_NOERR; break; /* directory */    
906
0
    case NC_NOERR: ret = NC_EEMPTY; goto done; 
907
0
    default: goto done;
908
0
    }
909
910
0
    dir = NCopendir(canonpath);
911
0
    if(dir == NULL)
912
0
        {ret = platformerr(errno); goto done;}
913
0
    for(;;) {
914
0
  const char* name = NULL;
915
0
  struct dirent* de = NULL;
916
0
  errno = 0;
917
0
        de = readdir(dir);
918
0
        if(de == NULL)
919
0
      {ret = platformerr(errno); goto done;}
920
0
  if(strcmp(de->d_name,".")==0 || strcmp(de->d_name,"..")==0)
921
0
      continue;
922
0
  name = de->d_name;
923
0
  nclistpush(contents,strdup(name));
924
0
    }
925
0
done:
926
0
    if(dir) NCclosedir(dir);
927
0
    errno = 0;
928
0
    return ZUNTRACEX(ret,"|contents|=%d",(int)nclistlength(contents));
929
0
}
930
#endif /*_WIN32*/
931
932
#if 0
933
static int
934
platformdeleter(ZFMAP* zfmap, NClist* segments, int depth)
935
{
936
    int ret = NC_NOERR;
937
    struct stat statbuf;
938
    struct dirent* entry = NULL;
939
    DIR* dir = NULL;
940
    char* path = NULL;
941
    char* tmp = NULL;
942
943
    if((ret = nczm_join(segments,&path))) goto done;
944
945
    /* When running on any platform that can accept drive letters */
946
    if((ret = nczm_fixpath(path,&tmp))) goto done;
947
    nullfree(path); path = NULL;
948
    if((path = NCpathcvt(tmp))==NULL) {ret = NC_ENOMEM; goto done;}
949
950
    errno = 0;
951
    ret = NCstat(path, &statbuf);
952
    if(ret < 0) {
953
        if(errno == ENOENT) {ret = NC_NOERR; goto done;}
954
  else {ret = platformerr(errno); goto done;}
955
    }
956
    /* process this file */
957
    if(S_ISDIR(statbuf.st_mode)) {
958
        if((dir = NCopendir(path)) == NULL)
959
       {ret = platformerr(errno); goto done;}
960
        for(;;) {
961
      char* seg = NULL;
962
      errno = 0;
963
            entry = readdir(dir);
964
      if(entry == NULL) {ret = platformerr(errno); break;}
965
      /* Ignore "." and ".." */
966
      if(strcmp(entry->d_name,".")==0) continue;
967
          if(strcmp(entry->d_name,"..")==0) continue;
968
      /* append name to segments */
969
      if((seg = strdup(entry->d_name)) == NULL)
970
    {ret = NC_ENOMEM; goto done;}
971
      nclistpush(segments,seg);
972
      /* recurse */
973
      if((ret = platformdeleter(zfmap, segments, depth+1))) goto done;
974
      /* remove+reclaim last segment */
975
      nclistpop(segments);
976
      nullfree(seg);            
977
        }
978
  /* Delete this file */
979
  rmdir(path);
980
    } else {
981
  assert(S_ISREG(statbuf.st_mode));
982
  unlink(path);
983
    }
984
done:
985
    if(dir) NCclosedir(dir);
986
    /* delete this file|dir */
987
    ret = NCremove(path);
988
    nullfree(path);
989
    nullfree(tmp);
990
    return ZUNTRACE(ret);
991
}
992
#endif /*0*/
993
994
static int
995
platformdeleter(ZFMAP* zfmap, NCbytes* canonpath, int delroot, int depth)
996
0
{
997
0
    int ret = NC_NOERR;
998
0
    int i;
999
0
    NClist* subfiles = nclistnew();
1000
0
    size_t tpathlen = ncbyteslength(canonpath);
1001
0
    char* local = NULL;
1002
1003
0
    local = ncbytescontents(canonpath);
1004
0
    ZTRACE(6,"map=%s canonpath=%s delroot=%d depth=%d",zfmap->map.url,local,delroot,depth);
1005
1006
0
    ret = platformdircontent(zfmap, local, subfiles);
1007
#ifdef DEBUG
1008
    {int i;
1009
  fprintf(stderr,"xxx: contents:\n");
1010
  for(i=0;i<nclistlength(contents);i++)
1011
      fprintf(stderr,"\t%s\n",(const char*)nclistget(contents,i));
1012
  fprintf(stderr,"xxx: end contents\n");
1013
    }
1014
#endif
1015
0
    switch (ret) {
1016
0
    case NC_NOERR: /* recurse to remove levels below */
1017
0
        for(i=0;i<nclistlength(subfiles);i++) {
1018
0
      const char* name = nclistget(subfiles,i);
1019
            /* append name to current path */
1020
0
            ncbytescat(canonpath, "/");
1021
0
            ncbytescat(canonpath, name);
1022
            /* recurse */
1023
0
            if ((ret = platformdeleter(zfmap, canonpath,delroot,depth+1))) goto done;
1024
0
            ncbytessetlength(canonpath,tpathlen); /* reset */
1025
0
      ncbytesnull(canonpath);
1026
0
      local = ncbytescontents(canonpath);
1027
0
  }
1028
0
  if(depth > 0 || delroot) {
1029
#ifdef DEBUG
1030
fprintf(stderr,"xxx: remove:  %s\n",canonpath);
1031
#endif
1032
0
            if(NCrmdir(local) < 0) { /* kill this dir */
1033
#ifdef DEBUG
1034
fprintf(stderr,"xxx: remove: errno=%d|%s\n",errno,nc_strerror(errno));
1035
#endif
1036
0
    ret = errno;
1037
0
    goto done;
1038
0
      }
1039
0
  }
1040
0
  break;    
1041
0
    case NC_EEMPTY: /* Not a directory */
1042
0
  ret = NC_NOERR;
1043
#ifdef DEBUG
1044
fprintf(stderr,"xxx: remove:  %s\n",canonpath);
1045
#endif
1046
0
        if(NCremove(local) < 0) {/* kill this file */
1047
#ifdef DEBUG
1048
fprintf(stderr,"xxx: remove: errno=%d|%s\n",errno,nc_strerror(errno));
1049
#endif
1050
0
      ret = errno;
1051
0
      goto done;
1052
0
  }
1053
0
  break;
1054
0
    case NC_ENOTFOUND:
1055
0
    default:
1056
0
  goto done;
1057
0
    }
1058
1059
0
done:
1060
0
    errno = 0;
1061
0
    nclistfreeall(subfiles);
1062
0
    ncbytessetlength(canonpath,tpathlen);
1063
0
    ncbytesnull(canonpath);
1064
0
    return ZUNTRACE(ret);
1065
0
}
1066
1067
/* Deep file/dir deletion; depth first */
1068
static int
1069
platformdelete(ZFMAP* zfmap, const char* rootpath, int delroot)
1070
0
{
1071
0
    int stat = NC_NOERR;
1072
0
    NCbytes* canonpath = ncbytesnew();
1073
1074
0
    ZTRACE(6,"map=%s rootpath=%s delroot=%d",zfmap->map.url,rootpath,delroot);
1075
    
1076
0
    if(rootpath == NULL || strlen(rootpath) == 0) goto done;
1077
0
    ncbytescat(canonpath,rootpath);
1078
0
    if(rootpath[strlen(rootpath)-1] == '/') /* elide trailing '/' */
1079
0
  ncbytessetlength(canonpath,ncbyteslength(canonpath)-1);
1080
0
    if((stat = platformdeleter(zfmap,canonpath,delroot,0))) goto done;
1081
0
done:
1082
0
    ncbytesfree(canonpath);
1083
0
    errno = 0;
1084
0
    return ZUNTRACE(stat);
1085
0
}
1086
1087
static int
1088
platformseek(ZFMAP* zfmap, FD* fd, int pos, size64_t* sizep)
1089
0
{
1090
0
    int ret = NC_NOERR;
1091
0
    off_t size, newsize;
1092
0
    struct stat statbuf;    
1093
    
1094
0
    assert(fd && fd->fd >= 0);
1095
    
1096
0
    ZTRACE(6,"map=%s fd=%d pos=%d",zfmap->map.url,(fd?fd->fd:-1),pos);
1097
1098
0
    errno = 0;
1099
0
    ret = NCfstat(fd->fd, &statbuf);    
1100
0
    if(ret < 0)
1101
0
  {ret = platformerr(errno); goto done;}
1102
0
    if(sizep) size = *sizep; else size = 0;
1103
0
    newsize = lseek(fd->fd,size,pos);
1104
0
    if(sizep) *sizep = newsize;
1105
0
done:
1106
0
    errno = 0;
1107
0
    return ZUNTRACEX(ret,"sizep=%llu",*sizep);
1108
0
}
1109
1110
static int
1111
platformread(ZFMAP* zfmap, FD* fd, size64_t count, void* content)
1112
0
{
1113
0
    int stat = NC_NOERR;
1114
0
    size_t need = count;
1115
0
    unsigned char* readpoint = content;
1116
1117
0
    assert(fd && fd->fd >= 0);
1118
1119
0
    ZTRACE(6,"map=%s fd=%d count=%llu",zfmap->map.url,(fd?fd->fd:-1),count);
1120
1121
0
    while(need > 0) {
1122
0
        ssize_t red;
1123
0
        if((red = read(fd->fd,readpoint,need)) <= 0)
1124
0
      {stat = errno; goto done;}
1125
0
        need -= red;
1126
0
  readpoint += red;
1127
0
    }
1128
0
done:
1129
0
    errno = 0;
1130
0
    return ZUNTRACE(stat);
1131
0
}
1132
1133
static int
1134
platformwrite(ZFMAP* zfmap, FD* fd, size64_t count, const void* content)
1135
0
{
1136
0
    int ret = NC_NOERR;
1137
0
    size_t need = count;
1138
0
    unsigned char* writepoint = (unsigned char*)content;
1139
1140
0
    assert(fd && fd->fd >= 0);
1141
    
1142
0
    ZTRACE(6,"map=%s fd=%d count=%llu",zfmap->map.url,(fd?fd->fd:-1),count);
1143
1144
0
    while(need > 0) {
1145
0
        ssize_t red = 0;
1146
0
        if((red = write(fd->fd,(void*)writepoint,need)) <= 0) 
1147
0
      {ret = NC_EACCESS; goto done;}
1148
0
        need -= red;
1149
0
  writepoint += red;
1150
0
    }
1151
0
done:
1152
0
    return ZUNTRACE(ret);
1153
0
}
1154
1155
#if 0
1156
static int
1157
platformcwd(char** cwdp)
1158
{
1159
    char buf[4096];
1160
    char* cwd = NULL;
1161
    cwd = NCcwd(buf,sizeof(buf));
1162
    if(cwd == NULL) return errno;
1163
    if(cwdp) *cwdp = strdup(buf);
1164
    return NC_NOERR;
1165
}
1166
#endif
1167
1168
/* When we are finished accessing FD; essentially
1169
   equivalent to closing the file descriptor.
1170
*/
1171
static void
1172
platformrelease(ZFMAP* zfmap, FD* fd)
1173
0
{
1174
0
    ZTRACE(6,"map=%s fd=%d",zfmap->map.url,(fd?fd->fd:-1));
1175
0
    if(fd->fd >=0) NCclose(fd->fd);
1176
0
    fd->fd = -1;
1177
0
    (void)ZUNTRACE(NC_NOERR);
1178
0
}
1179
1180
#if 0
1181
/* Close FD => return typ to FDNONE */
1182
*/
1183
static void
1184
platformclose(ZFMAP* zfmap, FD* fd)
1185
{
1186
    if(fd->typ == FDFILE) {
1187
        if(fd->fd >=0) close(fd->u,fd);
1188
  fd->fd = -1;
1189
    } else if(fd->type == FDDIR) {
1190
  if(fd->u.dir) NCclosedir(fd->u,dir);
1191
    }
1192
    fd->typ = FDNONE;
1193
}
1194
#endif
1195
1196
1197
#ifdef VERIFY
1198
static int
1199
verify(const char* path, int isdir)
1200
0
{
1201
0
    int ret = 0;
1202
0
    struct stat buf;
1203
1204
0
    ret = NCaccess(path,ACCESS_MODE_EXISTS);
1205
0
    if(ret < 0)
1206
0
        return 1; /* If it does not exist, then it can be anything */
1207
0
    ret = NCstat(path,&buf);
1208
0
    if(ret < 0) abort();
1209
0
    if(isdir && S_ISDIR(buf.st_mode)) return 1;
1210
0
    if(!isdir && S_ISREG(buf.st_mode)) return 1;           
1211
0
    return 0;
1212
0
}
1213
1214
static int
1215
verifykey(const char* key, int isdir)
1216
0
{
1217
0
    int ret = 0;
1218
0
    struct stat buf;
1219
1220
0
    if(key[0] == '/') key++; /* Want relative name */
1221
1222
0
    ret = NCaccess(key,ACCESS_MODE_EXISTS);
1223
0
    if(ret < 0)
1224
0
        return 1; /* If it does not exist, then it can be anything */
1225
0
    ret = NCstat(key,&buf);
1226
0
    if(ret < 0) abort();
1227
0
    if(isdir && S_ISDIR(buf.st_mode)) return 1;
1228
0
    if(!isdir && S_ISREG(buf.st_mode)) return 1;           
1229
0
    return 0;
1230
0
}
1231
#endif
1232
1233
#if 0
1234
/* Return NC_EINVAL if path does not exist; els 1/0 in isdirp and local path in canonpathp */
1235
static int
1236
testifdir(const char* path, int* isdirp, char** canonpathp)
1237
{
1238
    int ret = NC_NOERR;
1239
    char* tmp = NULL;
1240
    char* canonpath = NULL;
1241
    struct stat statbuf;
1242
1243
    /* Make path be windows compatible */
1244
    if((ret = nczm_fixpath(path,&tmp))) goto done;
1245
    if((canonpath = NCpathcvt(tmp))==NULL) {ret = NC_ENOMEM; goto done;}
1246
1247
    errno = 0;
1248
    ret = NCstat(canonpath, &statbuf);
1249
    if(ret < 0) {
1250
        if(errno == ENOENT)
1251
      ret = NC_ENOTFOUND;  /* path does not exist */
1252
  else
1253
      ret = platformerr(errno);
1254
  goto done;
1255
    }
1256
    /* Check for being a directory */
1257
    if(isdirp) {
1258
        if(S_ISDIR(statbuf.st_mode)) {*isdirp = 1;} else {*isdirp = 0;}
1259
    }
1260
    if(canonpathp) {*canonpathp = canonpath; canonpath = NULL;}
1261
done:
1262
    errno = 0;
1263
    nullfree(tmp);
1264
    nullfree(canonpath);
1265
    return ZUNTRACE(ret);    
1266
}
1267
#endif /* 0 */