/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 */ |