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