mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-27 07:30:33 +08:00
9380790ea8
re: The current netcdf-c release has some problems with the mingw platform on windows. Mostly they are path issues. Changes to support mingw+msys2: ------------------------------- * Enable option of looking into the windows registry to find the mingw root path. In aid of proper path handling. * Add mingw+msys as a specific platform in configure.ac and move testing of the platform to the front so it is available early. * Handle mingw X libncpoco (dynamic loader) properly even though mingw does not yet support it. * Handle mingw X plugins properly even though mingw does not yet support it. * Alias pwd='pwd -W' to better handle paths in shell scripts. * Plus a number of other minor compile irritations. * Disallow the use of multiple nc_open's on the same file for windows (and mingw) because windows does not seem to handle these properly. Not sure why we did not catch this earlier. * Add mountpoint info to dpathmgr.c to help support mingw. * Cleanup dpathmgr conversions. Known problems: --------------- * I have not been able to get shared libraries to work, so plugins/filters must be disabled. * There is some kind of problem with libcurl that I have not solved, so all uses of libcurl (currently DAP+Byterange) must be disabled. Misc. other fixes: ------------------ * Cleanup the relationship between ENABLE_PLUGINS and various other flags in CMakeLists.txt and configure.ac. * Re-arrange the TESTDIRS order in Makefile.am. * Add pseudo-breakpoint to nclog.[ch] for debugging. * Improve the documentation of the path manager code in ncpathmgr.h * Add better support for relative paths in dpathmgr.c * Default the mode args to NCfopen to include "b" (binary) for windows. * Add optional debugging output in various places. * Make sure that everything builds with plugins disabled. * Fix numerous (s)printf inconsistencies betweenb the format spec and the arguments.
1268 lines
32 KiB
C
Executable File
1268 lines
32 KiB
C
Executable File
/*
|
|
* Copyright 2018, University Corporation for Atmospheric Research
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
*/
|
|
|
|
#undef DEBUG
|
|
|
|
/* Not sure this has any effect */
|
|
#define _LARGEFILE_SOURCE 1
|
|
#define _LARGEFILE64_SOURCE 1
|
|
|
|
#include "zincludes.h"
|
|
|
|
#include <errno.h>
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_DIRENT_H
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#ifndef S_ISDIR
|
|
#define S_ISDIR(mode) ((mode) & _S_IFDIR)
|
|
#define S_ISREG(mode) ((mode) & _S_IFREG)
|
|
#endif
|
|
#if 0
|
|
#ifndef __cplusplus
|
|
#include <io.h>
|
|
#include <iostream>
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#include "fbits.h"
|
|
#include "ncpathmgr.h"
|
|
|
|
#define VERIFY
|
|
|
|
#ifndef O_DIRECTORY
|
|
# define O_DIRECTORY 0200000
|
|
#endif
|
|
|
|
/*Mnemonic*/
|
|
#define FLAG_ISDIR 1
|
|
#define FLAG_CREATE 1
|
|
#define SKIPLAST 1
|
|
#define WHOLEPATH 0
|
|
|
|
#define NCZM_FILE_V1 1
|
|
|
|
#ifdef S_IRUSR
|
|
static int NC_DEFAULT_CREATE_PERMS =
|
|
(S_IRUSR|S_IWUSR |S_IRGRP|S_IWGRP);
|
|
static int NC_DEFAULT_RWOPEN_PERMS =
|
|
(S_IRUSR|S_IWUSR |S_IRGRP|S_IWGRP);
|
|
static int NC_DEFAULT_ROPEN_PERMS =
|
|
// (S_IRUSR |S_IRGRP);
|
|
(S_IRUSR|S_IWUSR |S_IRGRP|S_IWGRP);
|
|
static int NC_DEFAULT_DIR_PERMS =
|
|
(S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IWGRP);
|
|
#else
|
|
static int NC_DEFAULT_CREATE_PERMS = 0660;
|
|
static int NC_DEFAULT_RWOPEN_PERMS = 0660;
|
|
static int NC_DEFAULT_ROPEN_PERMS = 0660;
|
|
static int NC_DEFAULT_DIR_PERMS = 0770;
|
|
#endif
|
|
|
|
/*
|
|
Do a simple mapping of our simplified map model
|
|
to a file system.
|
|
|
|
Every dataset is assumed to be rooted at some directory in the
|
|
file tree. So, its location is defined by some path to a
|
|
directory representing both the dataset and the root group of
|
|
that dataset. The root is recognized because it uniquely
|
|
contains a "superblock" file name ".nczarr" that provides
|
|
general information about a dataset. Nesting a dataset
|
|
inside a dataset is prohibited. This can be detected
|
|
by looking for an occurrence of a ".nczarr" file in any containing
|
|
directory. If such a file is found, then an illegal nested
|
|
dataset has been found.
|
|
|
|
For the object API, the mapping is as follows:
|
|
1. Every content-bearing object (e.g. .zgroup or .zarray) is mapped to a file.
|
|
The key constraint is that the content bearing objects are files.
|
|
This means that if a key points to a content bearing object then
|
|
no other key can have that content bearing key as a suffix.
|
|
2. The meta data containing files are assumed to contain
|
|
UTF-8 character data.
|
|
3. The chunk containing files are assumed to contain raw unsigned 8-bit byte data.
|
|
*/
|
|
|
|
/* define the var name containing an objects content */
|
|
#define ZCONTENT "data"
|
|
|
|
typedef struct FD {
|
|
int fd;
|
|
} FD;
|
|
|
|
static FD FDNUL = {-1};
|
|
|
|
/* Define the "subclass" of NCZMAP */
|
|
typedef struct ZFMAP {
|
|
NCZMAP map;
|
|
char* root;
|
|
} ZFMAP;
|
|
|
|
/* Forward */
|
|
static NCZMAP_API zapi;
|
|
static int zfileclose(NCZMAP* map, int delete);
|
|
static int zfcreategroup(ZFMAP*, const char* key, int nskip);
|
|
static int zflookupobj(ZFMAP*, const char* key, FD* fd);
|
|
static int zfparseurl(const char* path0, NCURI** urip);
|
|
static int zffullpath(ZFMAP* zfmap, const char* key, char**);
|
|
static void zfrelease(ZFMAP* zfmap, FD* fd);
|
|
static void zfunlink(const char* canonpath);
|
|
|
|
static int platformerr(int err);
|
|
static int platformcreatefile(ZFMAP* map, const char* truepath,FD*);
|
|
static int platformcreatedir(ZFMAP* map, const char* truepath);
|
|
static int platformopenfile(ZFMAP* zfmap, const char* truepath, FD* fd);
|
|
static int platformopendir(ZFMAP* map, const char* truepath);
|
|
static int platformdircontent(ZFMAP* map, const char* path, NClist* contents);
|
|
static int platformdelete(ZFMAP* map, const char* path, int delroot);
|
|
static int platformseek(ZFMAP* map, FD* fd, int pos, size64_t* offset);
|
|
static int platformread(ZFMAP* map, FD* fd, size64_t count, void* content);
|
|
static int platformwrite(ZFMAP* map, FD* fd, size64_t count, const void* content);
|
|
static void platformrelease(ZFMAP* zfmap, FD* fd);
|
|
static int platformtestcontentbearing(ZFMAP* zfmap, const char* truepath);
|
|
|
|
#ifdef VERIFY
|
|
static int verify(const char* path, int isdir);
|
|
static int verifykey(const char* key, int isdir);
|
|
#endif
|
|
|
|
static int zfinitialized = 0;
|
|
static void
|
|
zfileinitialize(void)
|
|
{
|
|
if(!zfinitialized) {
|
|
ZTRACE(5,NULL);
|
|
const char* env = NULL;
|
|
int perms = 0;
|
|
env = getenv("NC_DEFAULT_CREATE_PERMS");
|
|
if(env != NULL && strlen(env) > 0) {
|
|
if(sscanf(env,"%d",&perms) == 1) NC_DEFAULT_CREATE_PERMS = perms;
|
|
}
|
|
env = getenv("NC_DEFAULT_DIR_PERMS");
|
|
if(env != NULL && strlen(env) > 0) {
|
|
if(sscanf(env,"%d",&perms) == 1) NC_DEFAULT_DIR_PERMS = perms;
|
|
}
|
|
zfinitialized = 1;
|
|
(void)ZUNTRACE(NC_NOERR);
|
|
}
|
|
}
|
|
|
|
/* Define the Dataset level API */
|
|
|
|
/*
|
|
@param datasetpath abs path in the file tree of the root of the dataset'
|
|
might be a relative path.
|
|
@param mode the netcdf-c mode flags
|
|
@param flags extra flags
|
|
@param flags extra parameters
|
|
@param mapp return the map object in this
|
|
*/
|
|
|
|
static int
|
|
zfilecreate(const char *path, int mode, size64_t flags, void* parameters, NCZMAP** mapp)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* canonpath = NULL;
|
|
char* abspath = NULL;
|
|
ZFMAP* zfmap = NULL;
|
|
NCURI* url = NULL;
|
|
|
|
NC_UNUSED(parameters);
|
|
ZTRACE(5,"path=%s mode=%d flag=%llu",path,mode,flags);
|
|
|
|
if(!zfinitialized) zfileinitialize();
|
|
|
|
/* Fixup mode flags */
|
|
mode |= (NC_NETCDF4 | NC_WRITE);
|
|
|
|
if(!(mode & NC_WRITE))
|
|
{stat = NC_EPERM; goto done;}
|
|
|
|
/* path must be a url with file: protocol*/
|
|
if((stat=zfparseurl(path,&url)))
|
|
goto done;
|
|
if(strcasecmp(url->protocol,"file") != 0)
|
|
{stat = NC_EURL; goto done;}
|
|
|
|
/* Convert the root path */
|
|
if((canonpath = NCpathcvt(url->path))==NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
|
|
/* Make the root path be absolute */
|
|
if((abspath = NCpathabsolute(canonpath)) == NULL)
|
|
{stat = NC_EURL; goto done;}
|
|
|
|
/* Build the zmap state */
|
|
if((zfmap = calloc(1,sizeof(ZFMAP))) == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
|
|
zfmap->map.format = NCZM_FILE;
|
|
zfmap->map.url = ncuribuild(url,NULL,NULL,NCURIALL);
|
|
zfmap->map.flags = flags;
|
|
/* create => NC_WRITE */
|
|
zfmap->map.mode = mode;
|
|
zfmap->map.api = &zapi;
|
|
zfmap->root = abspath;
|
|
abspath = NULL;
|
|
|
|
/* If NC_CLOBBER, then delete below file tree */
|
|
if(!fIsSet(mode,NC_NOCLOBBER))
|
|
platformdelete(zfmap,zfmap->root,0);
|
|
|
|
/* make sure we can access the root directory; create if necessary */
|
|
if((stat = platformcreatedir(zfmap, zfmap->root)))
|
|
goto done;
|
|
|
|
/* Dataset superblock will be written by higher layer */
|
|
|
|
if(mapp) *mapp = (NCZMAP*)zfmap;
|
|
|
|
done:
|
|
ncurifree(url);
|
|
nullfree(canonpath);
|
|
nullfree(abspath);
|
|
if(stat)
|
|
zfileclose((NCZMAP*)zfmap,1);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
/*
|
|
@param datasetpath abs path in the file tree of the root of the dataset'
|
|
might be a relative path.
|
|
@param mode the netcdf-c mode flags
|
|
@param flags extra flags
|
|
@param flags extra parameters
|
|
@param mapp return the map object in this
|
|
*/
|
|
|
|
static int
|
|
zfileopen(const char *path, int mode, size64_t flags, void* parameters, NCZMAP** mapp)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* canonpath = NULL;
|
|
char* abspath = NULL;
|
|
ZFMAP* zfmap = NULL;
|
|
NCURI*url = NULL;
|
|
|
|
NC_UNUSED(parameters);
|
|
ZTRACE(5,"path=%s mode=%d flags=%llu",path,mode,flags);
|
|
|
|
if(!zfinitialized) zfileinitialize();
|
|
|
|
/* Fixup mode flags */
|
|
mode = (NC_NETCDF4 | mode);
|
|
|
|
/* path must be a url with file: protocol*/
|
|
if((stat=zfparseurl(path,&url)))
|
|
goto done;
|
|
if(strcasecmp(url->protocol,"file") != 0)
|
|
{stat = NC_EURL; goto done;}
|
|
|
|
/* Convert the root path */
|
|
if((canonpath = NCpathcvt(url->path))==NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
|
|
/* Make the root path be absolute */
|
|
if((abspath = NCpathabsolute(canonpath)) == NULL)
|
|
{stat = NC_EURL; goto done;}
|
|
|
|
/* Build the zmap state */
|
|
if((zfmap = calloc(1,sizeof(ZFMAP))) == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
|
|
zfmap->map.format = NCZM_FILE;
|
|
zfmap->map.url = ncuribuild(url,NULL,NULL,NCURIALL);
|
|
zfmap->map.flags = flags;
|
|
zfmap->map.mode = mode;
|
|
zfmap->map.api = (NCZMAP_API*)&zapi;
|
|
zfmap->root = abspath;
|
|
abspath = NULL;
|
|
|
|
/* Verify root dir exists */
|
|
if((stat = platformopendir(zfmap,zfmap->root)))
|
|
goto done;
|
|
|
|
/* Dataset superblock will be read by higher layer */
|
|
|
|
if(mapp) *mapp = (NCZMAP*)zfmap;
|
|
|
|
done:
|
|
ncurifree(url);
|
|
nullfree(canonpath);
|
|
nullfree(abspath);
|
|
if(stat) zfileclose((NCZMAP*)zfmap,0);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
/**************************************************/
|
|
/* Object API */
|
|
|
|
static int
|
|
zfileexists(NCZMAP* map, const char* key)
|
|
{
|
|
int stat = NC_NOERR;
|
|
ZFMAP* zfmap = (ZFMAP*)map;
|
|
FD fd = FDNUL;
|
|
|
|
ZTRACE(5,"map=%s key=%s",zfmap->map.url,key);
|
|
switch(stat=zflookupobj(zfmap,key,&fd)) {
|
|
case NC_NOERR: break;
|
|
case NC_ENOOBJECT: stat = NC_EEMPTY;
|
|
case NC_EEMPTY: break;
|
|
default: break;
|
|
}
|
|
zfrelease(zfmap,&fd);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
zfilelen(NCZMAP* map, const char* key, size64_t* lenp)
|
|
{
|
|
int stat = NC_NOERR;
|
|
ZFMAP* zfmap = (ZFMAP*)map;
|
|
size64_t len = 0;
|
|
FD fd = FDNUL;
|
|
|
|
ZTRACE(5,"map=%s key=%s",map->url,key);
|
|
|
|
switch (stat=zflookupobj(zfmap,key,&fd)) {
|
|
case NC_NOERR:
|
|
/* Get file size */
|
|
if((stat=platformseek(zfmap, &fd, SEEK_END, &len))) goto done;
|
|
break;
|
|
case NC_ENOOBJECT: stat = NC_EEMPTY;
|
|
case NC_EEMPTY: break;
|
|
default: break;
|
|
}
|
|
zfrelease(zfmap,&fd);
|
|
if(lenp) *lenp = len;
|
|
|
|
done:
|
|
return ZUNTRACEX(stat,"len=%llu",(lenp?*lenp:777777777777));
|
|
}
|
|
|
|
static int
|
|
zfileread(NCZMAP* map, const char* key, size64_t start, size64_t count, void* content)
|
|
{
|
|
int stat = NC_NOERR;
|
|
FD fd = FDNUL;
|
|
ZFMAP* zfmap = (ZFMAP*)map; /* cast to true type */
|
|
|
|
ZTRACE(5,"map=%s key=%s start=%llu count=%llu",map->url,key,start,count);
|
|
|
|
#ifdef VERIFY
|
|
if(!verifykey(key,!FLAG_ISDIR))
|
|
assert(!"expected file, have dir");
|
|
#endif
|
|
|
|
switch (stat = zflookupobj(zfmap,key,&fd)) {
|
|
case NC_NOERR:
|
|
if((stat = platformseek(zfmap, &fd, SEEK_SET, &start))) goto done;
|
|
if((stat = platformread(zfmap, &fd, count, content))) goto done;
|
|
break;
|
|
case NC_ENOOBJECT: stat = NC_EEMPTY;
|
|
case NC_EEMPTY: break;
|
|
default: break;
|
|
}
|
|
|
|
done:
|
|
zfrelease(zfmap,&fd);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
zfilewrite(NCZMAP* map, const char* key, size64_t start, size64_t count, const void* content)
|
|
{
|
|
int stat = NC_NOERR;
|
|
FD fd = FDNUL;
|
|
ZFMAP* zfmap = (ZFMAP*)map; /* cast to true type */
|
|
char* truepath = NULL;
|
|
|
|
ZTRACE(5,"map=%s key=%s start=%llu count=%llu",map->url,key,start,count);
|
|
|
|
#ifdef VERIFY
|
|
if(!verifykey(key,!FLAG_ISDIR))
|
|
assert(!"expected file, have dir");
|
|
#endif
|
|
|
|
switch (stat = zflookupobj(zfmap,key,&fd)) {
|
|
case NC_ENOOBJECT:
|
|
case NC_EEMPTY:
|
|
stat = NC_NOERR;
|
|
/* Create the directories leading to this */
|
|
if((stat = zfcreategroup(zfmap,key,SKIPLAST))) goto done;
|
|
/* Create truepath */
|
|
if((stat = zffullpath(zfmap,key,&truepath))) goto done;
|
|
/* Create file */
|
|
if((stat = platformcreatefile(zfmap,truepath,&fd))) goto done;
|
|
/* Fall thru to write the object */
|
|
case NC_NOERR:
|
|
if((stat = platformseek(zfmap, &fd, SEEK_SET, &start))) goto done;
|
|
if((stat = platformwrite(zfmap, &fd, count, content))) goto done;
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
done:
|
|
nullfree(truepath);
|
|
zfrelease(zfmap,&fd);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
zfileclose(NCZMAP* map, int delete)
|
|
{
|
|
int stat = NC_NOERR;
|
|
ZFMAP* zfmap = (ZFMAP*)map;
|
|
|
|
ZTRACE(5,"map=%s delete=%d",map->url,delete);
|
|
if(zfmap == NULL) return NC_NOERR;
|
|
|
|
/* Delete the subtree below the root and the root */
|
|
if(delete) {
|
|
stat = platformdelete(zfmap,zfmap->root,1);
|
|
zfunlink(zfmap->root);
|
|
}
|
|
nczm_clear(map);
|
|
nullfree(zfmap->root);
|
|
zfmap->root = NULL;
|
|
free(zfmap);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
/*
|
|
Return a list of names immediately "below" a specified prefix key.
|
|
In theory, the returned list should be sorted in lexical order,
|
|
but it possible that it is not.
|
|
The prefix key is not included.
|
|
*/
|
|
int
|
|
zfilesearch(NCZMAP* map, const char* prefixkey, NClist* matches)
|
|
{
|
|
int stat = NC_NOERR;
|
|
ZFMAP* zfmap = (ZFMAP*)map;
|
|
char* fullpath = NULL;
|
|
NClist* nextlevel = nclistnew();
|
|
NCbytes* buf = ncbytesnew();
|
|
|
|
ZTRACE(5,"map=%s prefixkey=%s",map->url,prefixkey);
|
|
|
|
/* Make the root path be true */
|
|
if(prefixkey == NULL || strlen(prefixkey)==0 || strcmp(prefixkey,"/")==0)
|
|
fullpath = strdup(zfmap->root);
|
|
else if((stat = nczm_concat(zfmap->root,prefixkey,&fullpath))) goto done;
|
|
|
|
/* get names of the next level path entries */
|
|
switch (stat = platformdircontent(zfmap, fullpath, nextlevel)) {
|
|
case NC_NOERR: /* ok */
|
|
break;
|
|
case NC_EEMPTY: /* not a dir */
|
|
stat = NC_NOERR;
|
|
goto done;
|
|
case NC_ENOOBJECT:
|
|
default:
|
|
goto done;
|
|
}
|
|
while(nclistlength(nextlevel) > 0) {
|
|
char* segment = nclistremove(nextlevel,0);
|
|
nclistpush(matches,segment);
|
|
}
|
|
|
|
done:
|
|
nclistfreeall(nextlevel);
|
|
ncbytesfree(buf);
|
|
nullfree(fullpath);
|
|
return ZUNTRACEX(stat,"|matches|=%d",(int)nclistlength(matches));
|
|
}
|
|
|
|
/**************************************************/
|
|
/* Utilities */
|
|
|
|
static void
|
|
zfunlink(const char* canonpath)
|
|
{
|
|
char* local = NULL;
|
|
if((local = NCpathcvt(canonpath))==NULL) goto done;
|
|
unlink(local);
|
|
done:
|
|
nullfree(local);
|
|
}
|
|
|
|
/* Lookup a group by parsed path (segments)*/
|
|
/* Return NC_EEMPTY if not found, NC_EINVAL if not a directory; create if create flag is set */
|
|
static int
|
|
zfcreategroup(ZFMAP* zfmap, const char* key, int nskip)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int i, len;
|
|
char* fullpath = NULL;
|
|
NCbytes* path = ncbytesnew();
|
|
NClist* segments = nclistnew();
|
|
|
|
ZTRACE(5,"map=%s key=%s nskip=%d",zfmap->map.url,key,nskip);
|
|
if((stat=nczm_split(key,segments)))
|
|
goto done;
|
|
len = nclistlength(segments);
|
|
len -= nskip; /* leave off last nskip segments */
|
|
ncbytescat(path,zfmap->root); /* We need path to be absolute */
|
|
for(i=0;i<len;i++) {
|
|
const char* seg = nclistget(segments,i);
|
|
ncbytescat(path,"/");
|
|
ncbytescat(path,seg);
|
|
/* open and optionally create the directory */
|
|
stat = platformcreatedir(zfmap,ncbytescontents(path));
|
|
if(stat) goto done;
|
|
}
|
|
done:
|
|
nullfree(fullpath);
|
|
ncbytesfree(path);
|
|
nclistfreeall(segments);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
/* Lookup an object
|
|
@return NC_NOERR if found and is a content-bearing object
|
|
@return NC_EEMPTY if exists but is not-content-bearing
|
|
@return NC_ENOOBJECT if not found
|
|
*/
|
|
static int
|
|
zflookupobj(ZFMAP* zfmap, const char* key, FD* fd)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* path = NULL;
|
|
|
|
ZTRACE(5,"map=%s key=%s",zfmap->map.url,key);
|
|
|
|
if((stat = zffullpath(zfmap,key,&path)))
|
|
{goto done;}
|
|
|
|
/* See if this is content-bearing */
|
|
if((stat = platformtestcontentbearing(zfmap,path)))
|
|
goto done;
|
|
|
|
/* Open the file */
|
|
if((stat = platformopenfile(zfmap,path,fd)))
|
|
goto done;
|
|
|
|
done:
|
|
errno = 0;
|
|
nullfree(path);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
/* When we are finished accessing object */
|
|
static void
|
|
zfrelease(ZFMAP* zfmap, FD* fd)
|
|
{
|
|
ZTRACE(5,"map=%s fd=%d",zfmap->map.url,(fd?fd->fd:-1));
|
|
platformrelease(zfmap,fd);
|
|
(void)ZUNTRACE(NC_NOERR);
|
|
}
|
|
|
|
/**************************************************/
|
|
/* External API objects */
|
|
|
|
NCZMAP_DS_API zmap_file = {
|
|
NCZM_FILE_V1,
|
|
0,
|
|
zfilecreate,
|
|
zfileopen,
|
|
};
|
|
|
|
static NCZMAP_API zapi = {
|
|
NCZM_FILE_V1,
|
|
zfileclose,
|
|
zfileexists,
|
|
zfilelen,
|
|
zfileread,
|
|
zfilewrite,
|
|
zfilesearch,
|
|
};
|
|
|
|
static int
|
|
zffullpath(ZFMAP* zfmap, const char* key, char** pathp)
|
|
{
|
|
int stat = NC_NOERR;
|
|
size_t klen, pxlen, flen;
|
|
char* path = NULL;
|
|
|
|
ZTRACE(6,"map=%s key=%s",zfmap->map.url,key);
|
|
|
|
klen = nulllen(key);
|
|
pxlen = strlen(zfmap->root);
|
|
flen = klen+pxlen+1+1;
|
|
if((path = malloc(flen)) == NULL) {stat = NC_ENOMEM; goto done;}
|
|
path[0] = '\0';
|
|
strlcat(path,zfmap->root,flen);
|
|
/* look for special cases */
|
|
if(key != NULL) {
|
|
if(key[0] != '/') strlcat(path,"/",flen);
|
|
if(strcmp(key,"/") != 0)
|
|
strlcat(path,key,flen);
|
|
}
|
|
if(pathp) {*pathp = path; path = NULL;}
|
|
done:
|
|
nullfree(path)
|
|
return ZUNTRACEX(stat,"path=%s",(pathp?*pathp:"null"));
|
|
}
|
|
|
|
static int
|
|
zfparseurl(const char* path0, NCURI** urip)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCURI* uri = NULL;
|
|
ZTRACE(6,"path0=%s",path0);
|
|
ncuriparse(path0,&uri);
|
|
if(uri == NULL)
|
|
{stat = NC_EURL; goto done;}
|
|
if(urip) {*urip = uri; uri = NULL;}
|
|
|
|
done:
|
|
ncurifree(uri);
|
|
return ZUNTRACEX(stat,"uri=%p",(urip?(void*)*urip:(void*)urip));
|
|
return stat;
|
|
}
|
|
|
|
/**************************************************/
|
|
static int
|
|
platformerr(int err)
|
|
{
|
|
ZTRACE(6,"err=%d",err);
|
|
switch (err) {
|
|
case ENOENT: err = NC_ENOOBJECT; break; /* File does not exist */
|
|
case ENOTDIR: err = NC_EEMPTY; break; /* no content */
|
|
case EACCES: err = NC_EAUTH; break; /* file permissions */
|
|
case EPERM: err = NC_EAUTH; break; /* ditto */
|
|
default: break;
|
|
}
|
|
return ZUNTRACE(err);
|
|
}
|
|
|
|
/* Test type of the specified file.
|
|
@return NC_NOERR if found and is a content-bearing object (file)
|
|
@return NC_EEMPTY if exists but is not-content-bearing (a directory)
|
|
@return NC_ENOOBJECT if not found
|
|
*/
|
|
static int
|
|
platformtestcontentbearing(ZFMAP* zfmap, const char* canonpath)
|
|
{
|
|
int ret = 0;
|
|
struct stat buf;
|
|
|
|
ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
|
|
|
|
errno = 0;
|
|
ret = NCstat(canonpath, &buf);
|
|
ZTRACEMORE(6,"\tstat: ret=%d, errno=%d st_mode=%d",ret,errno,buf.st_mode);
|
|
if(ret < 0) {
|
|
ret = platformerr(errno);
|
|
} else if(S_ISDIR(buf.st_mode)) {
|
|
ret = NC_EEMPTY;
|
|
} else
|
|
ret = NC_NOERR;
|
|
errno = 0;
|
|
return ZUNTRACE(ret);
|
|
}
|
|
|
|
/* Create a file */
|
|
static int
|
|
platformcreatefile(ZFMAP* zfmap, const char* canonpath, FD* fd)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int ioflags = 0;
|
|
int createflags = 0;
|
|
int mode = zfmap->map.mode;
|
|
int permissions = NC_DEFAULT_ROPEN_PERMS;
|
|
|
|
ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
|
|
|
|
errno = 0;
|
|
if(!fIsSet(mode, NC_WRITE))
|
|
ioflags |= (O_RDONLY);
|
|
else {
|
|
ioflags |= (O_RDWR);
|
|
permissions = NC_DEFAULT_RWOPEN_PERMS;
|
|
}
|
|
#ifdef O_BINARY
|
|
fSet(ioflags, O_BINARY);
|
|
#endif
|
|
|
|
if(fIsSet(mode, NC_NOCLOBBER))
|
|
fSet(createflags, O_EXCL);
|
|
else
|
|
fSet(createflags, O_TRUNC);
|
|
|
|
if(fIsSet(mode,NC_WRITE))
|
|
createflags = (ioflags|O_CREAT);
|
|
|
|
/* Try to create file (will also NCpathcvt) */
|
|
fd->fd = NCopen3(canonpath, createflags, permissions);
|
|
if(fd->fd < 0) { /* could not create */
|
|
stat = platformerr(errno);
|
|
goto done; /* could not open */
|
|
}
|
|
done:
|
|
errno = 0;
|
|
return ZUNTRACEX(stat,"fd=%d",(fd?fd->fd:-1));
|
|
}
|
|
|
|
/* Open a file; fail if it does not exist */
|
|
static int
|
|
platformopenfile(ZFMAP* zfmap, const char* canonpath, FD* fd)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int ioflags = 0;
|
|
int mode = zfmap->map.mode;
|
|
int permissions = 0;
|
|
|
|
ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
|
|
|
|
errno = 0;
|
|
if(!fIsSet(mode, NC_WRITE)) {
|
|
ioflags |= (O_RDONLY);
|
|
permissions = NC_DEFAULT_ROPEN_PERMS;
|
|
} else {
|
|
ioflags |= (O_RDWR);
|
|
permissions = NC_DEFAULT_RWOPEN_PERMS;
|
|
}
|
|
#ifdef O_BINARY
|
|
fSet(ioflags, O_BINARY);
|
|
#endif
|
|
|
|
#ifdef VERIFY
|
|
if(!verify(canonpath,!FLAG_ISDIR))
|
|
assert(!"expected file, have dir");
|
|
#endif
|
|
|
|
/* Try to open file (will localize) */
|
|
fd->fd = NCopen3(canonpath, ioflags, permissions);
|
|
if(fd->fd < 0)
|
|
{stat = platformerr(errno); goto done;} /* could not open */
|
|
done:
|
|
errno = 0;
|
|
return ZUNTRACEX(stat,"fd=%d",(fd?fd->fd:-1));
|
|
}
|
|
|
|
/* Create a dir */
|
|
static int
|
|
platformcreatedir(ZFMAP* zfmap, const char* canonpath)
|
|
{
|
|
int ret = NC_NOERR;
|
|
int mode = zfmap->map.mode;
|
|
|
|
ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
|
|
|
|
errno = 0;
|
|
/* Try to access file as if it exists */
|
|
ret = NCaccess(canonpath,ACCESS_MODE_EXISTS);
|
|
if(ret < 0) { /* it does not exist, then it can be anything */
|
|
if(fIsSet(mode,NC_WRITE)) {
|
|
/* Try to create it */
|
|
/* Create the directory using mkdir */
|
|
if(NCmkdir(canonpath,NC_DEFAULT_DIR_PERMS) < 0)
|
|
{ret = platformerr(errno); goto done;}
|
|
/* try to access again */
|
|
ret = NCaccess(canonpath,ACCESS_MODE_EXISTS);
|
|
if(ret < 0)
|
|
{ret = platformerr(errno); goto done;}
|
|
} else
|
|
{ret = platformerr(errno); goto done;}
|
|
}
|
|
|
|
done:
|
|
errno = 0;
|
|
return ZUNTRACE(ret);
|
|
}
|
|
|
|
/* Open a dir; fail if it does not exist */
|
|
static int
|
|
platformopendir(ZFMAP* zfmap, const char* canonpath)
|
|
{
|
|
int ret = NC_NOERR;
|
|
|
|
ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
|
|
|
|
errno = 0;
|
|
/* Try to access file as if it exists */
|
|
ret = NCaccess(canonpath,ACCESS_MODE_EXISTS);
|
|
if(ret < 0)
|
|
{ret = platformerr(errno); goto done;}
|
|
done:
|
|
errno = 0;
|
|
return ZUNTRACE(ret);
|
|
}
|
|
|
|
/**
|
|
Given a path, return the list of all files+dirs immediately below
|
|
the specified path: e.g. X s.t. path/X exists.
|
|
There are several possibilities:
|
|
1. path does not exist => return NC_ENOTFOUND
|
|
2. path is not a directory => return NC_EEMPTY and |contents| == 0
|
|
3. path is a directory => return NC_NOERR and |contents| >= 0
|
|
|
|
@return NC_NOERR if path is a directory
|
|
@return NC_EEMPTY if path is not a directory
|
|
@return NC_ENOTFOUND if path does not exist
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
static int
|
|
platformdircontent(ZFMAP* zfmap, const char* canonpath, NClist* contents)
|
|
{
|
|
int ret = NC_NOERR;
|
|
errno = 0;
|
|
WIN32_FIND_DATA FindFileData;
|
|
HANDLE dir = NULL;
|
|
char* ffpath = NULL;
|
|
char* lpath = NULL;
|
|
size_t len;
|
|
char* d = NULL;
|
|
|
|
ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
|
|
|
|
switch (ret = platformtestcontentbearing(zfmap, canonpath)) {
|
|
case NC_EEMPTY: ret = NC_NOERR; break; /* directory */
|
|
case NC_NOERR: ret = NC_EEMPTY; goto done;
|
|
default: goto done;
|
|
}
|
|
|
|
/* We need to process the path to make it work with FindFirstFile */
|
|
len = strlen(canonpath);
|
|
/* Need to terminate path with '/''*' */
|
|
ffpath = (char*)malloc(len+2+1);
|
|
memcpy(ffpath,canonpath,len);
|
|
if(canonpath[len-1] != '/') {
|
|
ffpath[len] = '/';
|
|
len++;
|
|
}
|
|
ffpath[len] = '*'; len++;
|
|
ffpath[len] = '\0';
|
|
|
|
/* localize it */
|
|
if((lpath = NCpathcvt(ffpath))==NULL)
|
|
{ret = NC_ENOMEM; goto done;}
|
|
dir = FindFirstFile(lpath, &FindFileData);
|
|
if(dir == INVALID_HANDLE_VALUE) {
|
|
/* Distinquish not-a-directory from no-matching-file */
|
|
switch (GetLastError()) {
|
|
case ERROR_FILE_NOT_FOUND: /* No matching files */ /* fall thru */
|
|
ret = NC_NOERR;
|
|
goto done;
|
|
case ERROR_DIRECTORY: /* not a directory */
|
|
default:
|
|
ret = NC_EEMPTY;
|
|
goto done;
|
|
}
|
|
}
|
|
do {
|
|
const char* name = NULL;
|
|
name = FindFileData.cFileName;
|
|
if(strcmp(name,".")==0 || strcmp(name,"..")==0)
|
|
continue;
|
|
nclistpush(contents,strdup(name));
|
|
} while(FindNextFile(dir, &FindFileData));
|
|
|
|
done:
|
|
if(dir) FindClose(dir);
|
|
nullfree(lpath);
|
|
nullfree(ffpath);
|
|
nullfree(d);
|
|
errno = 0;
|
|
return ZUNTRACEX(ret,"|contents|=%d",(int)nclistlength(contents));
|
|
}
|
|
|
|
#else /*!_WIN32*/
|
|
|
|
static int
|
|
platformdircontent(ZFMAP* zfmap, const char* canonpath, NClist* contents)
|
|
{
|
|
int ret = NC_NOERR;
|
|
errno = 0;
|
|
DIR* dir = NULL;
|
|
|
|
ZTRACE(6,"map=%s canonpath=%s",zfmap->map.url,canonpath);
|
|
|
|
switch (ret = platformtestcontentbearing(zfmap, canonpath)) {
|
|
case NC_EEMPTY: ret = NC_NOERR; break; /* directory */
|
|
case NC_NOERR: ret = NC_EEMPTY; goto done;
|
|
default: goto done;
|
|
}
|
|
|
|
dir = NCopendir(canonpath);
|
|
if(dir == NULL)
|
|
{ret = platformerr(errno); goto done;}
|
|
for(;;) {
|
|
const char* name = NULL;
|
|
struct dirent* de = NULL;
|
|
errno = 0;
|
|
de = readdir(dir);
|
|
if(de == NULL)
|
|
{ret = platformerr(errno); goto done;}
|
|
if(strcmp(de->d_name,".")==0 || strcmp(de->d_name,"..")==0)
|
|
continue;
|
|
name = de->d_name;
|
|
nclistpush(contents,strdup(name));
|
|
}
|
|
done:
|
|
if(dir) NCclosedir(dir);
|
|
errno = 0;
|
|
return ZUNTRACEX(ret,"|contents|=%d",(int)nclistlength(contents));
|
|
}
|
|
#endif /*_WIN32*/
|
|
|
|
#if 0
|
|
static int
|
|
platformdeleter(ZFMAP* zfmap, NClist* segments, int depth)
|
|
{
|
|
int ret = NC_NOERR;
|
|
struct stat statbuf;
|
|
struct dirent* entry = NULL;
|
|
DIR* dir = NULL;
|
|
char* path = NULL;
|
|
char* tmp = NULL;
|
|
|
|
if((ret = nczm_join(segments,&path))) goto done;
|
|
|
|
/* When running on any platform that can accept drive letters */
|
|
if((ret = nczm_fixpath(path,&tmp))) goto done;
|
|
nullfree(path); path = NULL;
|
|
if((path = NCpathcvt(tmp))==NULL) {ret = NC_ENOMEM; goto done;}
|
|
|
|
errno = 0;
|
|
ret = NCstat(path, &statbuf);
|
|
if(ret < 0) {
|
|
if(errno == ENOENT) {ret = NC_NOERR; goto done;}
|
|
else {ret = platformerr(errno); goto done;}
|
|
}
|
|
/* process this file */
|
|
if(S_ISDIR(statbuf.st_mode)) {
|
|
if((dir = NCopendir(path)) == NULL)
|
|
{ret = platformerr(errno); goto done;}
|
|
for(;;) {
|
|
char* seg = NULL;
|
|
errno = 0;
|
|
entry = readdir(dir);
|
|
if(entry == NULL) {ret = platformerr(errno); break;}
|
|
/* Ignore "." and ".." */
|
|
if(strcmp(entry->d_name,".")==0) continue;
|
|
if(strcmp(entry->d_name,"..")==0) continue;
|
|
/* append name to segments */
|
|
if((seg = strdup(entry->d_name)) == NULL)
|
|
{ret = NC_ENOMEM; goto done;}
|
|
nclistpush(segments,seg);
|
|
/* recurse */
|
|
if((ret = platformdeleter(zfmap, segments, depth+1))) goto done;
|
|
/* remove+reclaim last segment */
|
|
nclistpop(segments);
|
|
nullfree(seg);
|
|
}
|
|
/* Delete this file */
|
|
rmdir(path);
|
|
} else {
|
|
assert(S_ISREG(statbuf.st_mode));
|
|
unlink(path);
|
|
}
|
|
done:
|
|
if(dir) NCclosedir(dir);
|
|
/* delete this file|dir */
|
|
ret = NCremove(path);
|
|
nullfree(path);
|
|
nullfree(tmp);
|
|
return ZUNTRACE(ret);
|
|
}
|
|
#endif /*0*/
|
|
|
|
static int
|
|
platformdeleter(ZFMAP* zfmap, NCbytes* canonpath, int delroot, int depth)
|
|
{
|
|
int ret = NC_NOERR;
|
|
int i;
|
|
NClist* subfiles = nclistnew();
|
|
size_t tpathlen = ncbyteslength(canonpath);
|
|
char* local = NULL;
|
|
|
|
local = ncbytescontents(canonpath);
|
|
ZTRACE(6,"map=%s canonpath=%s delroot=%d depth=%d",zfmap->map.url,local,delroot,depth);
|
|
|
|
ret = platformdircontent(zfmap, local, subfiles);
|
|
#ifdef DEBUG
|
|
{int i;
|
|
fprintf(stderr,"xxx: contents:\n");
|
|
for(i=0;i<nclistlength(contents);i++)
|
|
fprintf(stderr,"\t%s\n",(const char*)nclistget(contents,i));
|
|
fprintf(stderr,"xxx: end contents\n");
|
|
}
|
|
#endif
|
|
switch (ret) {
|
|
case NC_NOERR: /* recurse to remove levels below */
|
|
for(i=0;i<nclistlength(subfiles);i++) {
|
|
const char* name = nclistget(subfiles,i);
|
|
/* append name to current path */
|
|
ncbytescat(canonpath, "/");
|
|
ncbytescat(canonpath, name);
|
|
/* recurse */
|
|
if ((ret = platformdeleter(zfmap, canonpath,delroot,depth+1))) goto done;
|
|
ncbytessetlength(canonpath,tpathlen); /* reset */
|
|
ncbytesnull(canonpath);
|
|
local = ncbytescontents(canonpath);
|
|
}
|
|
if(depth > 0 || delroot) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"xxx: remove: %s\n",canonpath);
|
|
#endif
|
|
if(NCrmdir(local) < 0) { /* kill this dir */
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"xxx: remove: errno=%d|%s\n",errno,nc_strerror(errno));
|
|
#endif
|
|
ret = errno;
|
|
goto done;
|
|
}
|
|
}
|
|
break;
|
|
case NC_EEMPTY: /* Not a directory */
|
|
ret = NC_NOERR;
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"xxx: remove: %s\n",canonpath);
|
|
#endif
|
|
if(NCremove(local) < 0) {/* kill this file */
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"xxx: remove: errno=%d|%s\n",errno,nc_strerror(errno));
|
|
#endif
|
|
ret = errno;
|
|
goto done;
|
|
}
|
|
break;
|
|
case NC_ENOTFOUND:
|
|
default:
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
errno = 0;
|
|
nclistfreeall(subfiles);
|
|
ncbytessetlength(canonpath,tpathlen);
|
|
ncbytesnull(canonpath);
|
|
return ZUNTRACE(ret);
|
|
}
|
|
|
|
/* Deep file/dir deletion; depth first */
|
|
static int
|
|
platformdelete(ZFMAP* zfmap, const char* rootpath, int delroot)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCbytes* canonpath = ncbytesnew();
|
|
|
|
ZTRACE(6,"map=%s rootpath=%s delroot=%d",zfmap->map.url,rootpath,delroot);
|
|
|
|
if(rootpath == NULL || strlen(rootpath) == 0) goto done;
|
|
ncbytescat(canonpath,rootpath);
|
|
if(rootpath[strlen(rootpath)-1] == '/') /* elide trailing '/' */
|
|
ncbytessetlength(canonpath,ncbyteslength(canonpath)-1);
|
|
if((stat = platformdeleter(zfmap,canonpath,delroot,0))) goto done;
|
|
done:
|
|
ncbytesfree(canonpath);
|
|
errno = 0;
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
platformseek(ZFMAP* zfmap, FD* fd, int pos, size64_t* sizep)
|
|
{
|
|
int ret = NC_NOERR;
|
|
off_t size, newsize;
|
|
struct stat statbuf;
|
|
|
|
assert(fd && fd->fd >= 0);
|
|
|
|
ZTRACE(6,"map=%s fd=%d pos=%d",zfmap->map.url,(fd?fd->fd:-1),pos);
|
|
|
|
errno = 0;
|
|
ret = NCfstat(fd->fd, &statbuf);
|
|
if(ret < 0)
|
|
{ret = platformerr(errno); goto done;}
|
|
if(sizep) size = *sizep; else size = 0;
|
|
newsize = lseek(fd->fd,size,pos);
|
|
if(sizep) *sizep = newsize;
|
|
done:
|
|
errno = 0;
|
|
return ZUNTRACEX(ret,"sizep=%llu",*sizep);
|
|
}
|
|
|
|
static int
|
|
platformread(ZFMAP* zfmap, FD* fd, size64_t count, void* content)
|
|
{
|
|
int stat = NC_NOERR;
|
|
size_t need = count;
|
|
unsigned char* readpoint = content;
|
|
|
|
assert(fd && fd->fd >= 0);
|
|
|
|
ZTRACE(6,"map=%s fd=%d count=%llu",zfmap->map.url,(fd?fd->fd:-1),count);
|
|
|
|
while(need > 0) {
|
|
ssize_t red;
|
|
if((red = read(fd->fd,readpoint,need)) <= 0)
|
|
{stat = errno; goto done;}
|
|
need -= red;
|
|
readpoint += red;
|
|
}
|
|
done:
|
|
errno = 0;
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
platformwrite(ZFMAP* zfmap, FD* fd, size64_t count, const void* content)
|
|
{
|
|
int ret = NC_NOERR;
|
|
size_t need = count;
|
|
unsigned char* writepoint = (unsigned char*)content;
|
|
|
|
assert(fd && fd->fd >= 0);
|
|
|
|
ZTRACE(6,"map=%s fd=%d count=%llu",zfmap->map.url,(fd?fd->fd:-1),count);
|
|
|
|
while(need > 0) {
|
|
ssize_t red = 0;
|
|
if((red = write(fd->fd,(void*)writepoint,need)) <= 0)
|
|
{ret = NC_EACCESS; goto done;}
|
|
need -= red;
|
|
writepoint += red;
|
|
}
|
|
done:
|
|
return ZUNTRACE(ret);
|
|
}
|
|
|
|
#if 0
|
|
static int
|
|
platformcwd(char** cwdp)
|
|
{
|
|
char buf[4096];
|
|
char* cwd = NULL;
|
|
cwd = NCcwd(buf,sizeof(buf));
|
|
if(cwd == NULL) return errno;
|
|
if(cwdp) *cwdp = strdup(buf);
|
|
return NC_NOERR;
|
|
}
|
|
#endif
|
|
|
|
/* When we are finished accessing FD; essentially
|
|
equivalent to closing the file descriptor.
|
|
*/
|
|
static void
|
|
platformrelease(ZFMAP* zfmap, FD* fd)
|
|
{
|
|
ZTRACE(6,"map=%s fd=%d",zfmap->map.url,(fd?fd->fd:-1));
|
|
if(fd->fd >=0) NCclose(fd->fd);
|
|
fd->fd = -1;
|
|
(void)ZUNTRACE(NC_NOERR);
|
|
}
|
|
|
|
#if 0
|
|
/* Close FD => return typ to FDNONE */
|
|
*/
|
|
static void
|
|
platformclose(ZFMAP* zfmap, FD* fd)
|
|
{
|
|
if(fd->typ == FDFILE) {
|
|
if(fd->fd >=0) close(fd->u,fd);
|
|
fd->fd = -1;
|
|
} else if(fd->type == FDDIR) {
|
|
if(fd->u.dir) NCclosedir(fd->u,dir);
|
|
}
|
|
fd->typ = FDNONE;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef VERIFY
|
|
static int
|
|
verify(const char* path, int isdir)
|
|
{
|
|
int ret = 0;
|
|
struct stat buf;
|
|
|
|
ret = NCaccess(path,ACCESS_MODE_EXISTS);
|
|
if(ret < 0)
|
|
return 1; /* If it does not exist, then it can be anything */
|
|
ret = NCstat(path,&buf);
|
|
if(ret < 0) abort();
|
|
if(isdir && S_ISDIR(buf.st_mode)) return 1;
|
|
if(!isdir && S_ISREG(buf.st_mode)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
verifykey(const char* key, int isdir)
|
|
{
|
|
int ret = 0;
|
|
struct stat buf;
|
|
|
|
if(key[0] == '/') key++; /* Want relative name */
|
|
|
|
ret = NCaccess(key,ACCESS_MODE_EXISTS);
|
|
if(ret < 0)
|
|
return 1; /* If it does not exist, then it can be anything */
|
|
ret = NCstat(key,&buf);
|
|
if(ret < 0) abort();
|
|
if(isdir && S_ISDIR(buf.st_mode)) return 1;
|
|
if(!isdir && S_ISREG(buf.st_mode)) return 1;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
/* Return NC_EINVAL if path does not exist; els 1/0 in isdirp and local path in canonpathp */
|
|
static int
|
|
testifdir(const char* path, int* isdirp, char** canonpathp)
|
|
{
|
|
int ret = NC_NOERR;
|
|
char* tmp = NULL;
|
|
char* canonpath = NULL;
|
|
struct stat statbuf;
|
|
|
|
/* Make path be windows compatible */
|
|
if((ret = nczm_fixpath(path,&tmp))) goto done;
|
|
if((canonpath = NCpathcvt(tmp))==NULL) {ret = NC_ENOMEM; goto done;}
|
|
|
|
errno = 0;
|
|
ret = NCstat(canonpath, &statbuf);
|
|
if(ret < 0) {
|
|
if(errno == ENOENT)
|
|
ret = NC_ENOTFOUND; /* path does not exist */
|
|
else
|
|
ret = platformerr(errno);
|
|
goto done;
|
|
}
|
|
/* Check for being a directory */
|
|
if(isdirp) {
|
|
if(S_ISDIR(statbuf.st_mode)) {*isdirp = 1;} else {*isdirp = 0;}
|
|
}
|
|
if(canonpathp) {*canonpathp = canonpath; canonpath = NULL;}
|
|
done:
|
|
errno = 0;
|
|
nullfree(tmp);
|
|
nullfree(canonpath);
|
|
return ZUNTRACE(ret);
|
|
}
|
|
#endif /* 0 */
|