1 /* HTFile.c
2 ** FILE ACCESS
3 **
4 ** (c) COPYRIGHT MIT 1995.
5 ** Please first read the full copyright statement in the file COPYRIGH.
6 ** @(#) $Id$
7 **
8 ** This is unix-specific code in general, with some VMS bits.
9 ** These are routines for file access used by browsers.
10 **
11 ** History:
12 ** Feb 91 Written Tim Berners-Lee CERN/CN
13 ** Apr 91 vms-vms access included using DECnet syntax
14 ** 26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
15 ** Fixed access bug for relative names on VMS.
16 ** Sep 93 (MD) Access to VMS files allows sharing.
17 ** 15 Nov 93 (MD) Moved HTVMSname to HTVMSUTILS.C
18 ** 22 Feb 94 (MD) Excluded two routines if we are not READING directories
19 ** 18 May 94 (HF) Directory stuff removed and stream handling updated,
20 ** error messages introduced etc.
21 ** HFN Separated transport
22 **
23 ** Bugs:
24 ** FTP: Cannot access VMS files from a unix machine.
25 ** How can we know that the
26 ** target machine runs VMS?
27 */
28
29 /* Library Includes */
30 #include "wwwsys.h"
31 #include "WWWUtil.h"
32 #include "WWWCore.h"
33 #include "WWWDir.h"
34 #include "WWWTrans.h"
35 #include "HTReqMan.h"
36 #include "HTBind.h"
37 #include "HTMulti.h"
38 #include "HTFile.h" /* Implemented here */
39
40 /* Final states have negative value */
41 typedef enum _FileState {
42 FS_RETRY = -4,
43 FS_ERROR = -3,
44 FS_NO_DATA = -2,
45 FS_GOT_DATA = -1,
46 FS_BEGIN = 0,
47 FS_PENDING,
48 FS_DO_CN,
49 FS_NEED_OPEN_FILE,
50 FS_NEED_BODY,
51 FS_PARSE_DIR,
52 FS_TRY_FTP
53 } FileState;
54
55 /* This is the context structure for the this module */
56 typedef struct _file_info {
57 FileState state; /* Current state of the connection */
58 char * local; /* Local representation of file name */
59 struct stat stat_info; /* Contains actual file chosen */
60 HTNet * net;
61 HTTimer * timer;
62 } file_info;
63
64 struct _HTStream {
65 const HTStreamClass * isa;
66 };
67
68 struct _HTInputStream {
69 const HTInputStreamClass * isa;
70 };
71
72 PRIVATE HTDirReadme dir_readme = HT_DIR_README_TOP;
73 PRIVATE HTDirAccess dir_access = HT_DIR_OK;
74 PRIVATE HTDirShow dir_show = HT_DS_SIZE+HT_DS_DATE+HT_DS_DES+HT_DS_ICON;
75 PRIVATE HTDirKey dir_key = HT_DK_CINS;
76 PRIVATE BOOL file_suffix_binding = YES;
77
78 /* ------------------------------------------------------------------------- */
79
80 /* Directory Access
81 ** ----------------
82 */
HTFile_setDirAccess(HTDirAccess mode)83 PUBLIC BOOL HTFile_setDirAccess (HTDirAccess mode)
84 {
85 dir_access = mode;
86 return YES;
87 }
88
HTFile_dirAccess(void)89 PUBLIC HTDirAccess HTFile_dirAccess (void)
90 {
91 return dir_access;
92 }
93
94 /* Directory Readme
95 ** ----------------
96 */
HTFile_setDirReadme(HTDirReadme mode)97 PUBLIC BOOL HTFile_setDirReadme (HTDirReadme mode)
98 {
99 dir_readme = mode;
100 return YES;
101 }
102
HTFile_dirReadme(void)103 PUBLIC HTDirReadme HTFile_dirReadme (void)
104 {
105 return dir_readme;
106 }
107
108 /*
109 ** Should we find the bindings between file suffixes and media types
110 ** here or not?
111 */
HTFile_doFileSuffixBinding(BOOL mode)112 PUBLIC BOOL HTFile_doFileSuffixBinding (BOOL mode)
113 {
114 file_suffix_binding = mode;
115 return YES;
116 }
117
HTFile_fileSuffixBinding(void)118 PUBLIC BOOL HTFile_fileSuffixBinding (void)
119 {
120 return file_suffix_binding;
121 }
122
123 /* HTFile_readDir
124 ** --------------
125 ** Reads the directory "path"
126 ** Returns:
127 ** HT_ERROR Error
128 ** HT_FORBIDDEN Directory reading not allowed
129 ** HT_LOADED Successfully read the directory
130 */
HTFile_readDir(HTRequest * request,file_info * file)131 PRIVATE int HTFile_readDir (HTRequest * request, file_info *file)
132 {
133 #ifdef HAVE_READDIR
134 DIR * dp;
135 struct stat file_info;
136 HTParentAnchor * anchor = HTRequest_anchor(request);
137 char *url = HTAnchor_physical(anchor);
138 char fullname[HT_MAX_PATH+1];
139 char *name;
140 HTTRACE(PROT_TRACE, "Reading..... directory\n");
141 if (dir_access == HT_DIR_FORBID) {
142 HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN,
143 NULL, 0, "HTFile_readDir");
144 return HT_FORBIDDEN;
145 }
146
147 /* Initialize path name for stat() */
148 if (*(name = (url+strlen(url)-1)) != '/') {
149 char *newurl = NULL;
150 StrAllocCopy(newurl, url);
151 StrAllocCat(newurl, "/");
152 HT_FREE(file->local);
153 file->local = HTWWWToLocal(newurl, "", HTRequest_userProfile(request));
154 HT_FREE(newurl);
155 }
156 strcpy(fullname, file->local);
157 name = fullname+strlen(fullname); /* Point to end of fullname */
158
159 /* Check if access is enabled */
160 if (dir_access == HT_DIR_SELECTIVE) {
161 strcpy(name, DEFAULT_DIR_FILE);
162 if (HT_STAT(fullname, &file_info)) {
163 HTTRACE(PROT_TRACE, "Read dir.... `%s\' not found\n" _ DEFAULT_DIR_FILE);
164 HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN,
165 NULL, 0, "HTFile_readDir");
166 return HT_FORBIDDEN;
167 }
168 }
169
170 if ((dp = opendir(file->local))) {
171 struct dirent * dirbuf;
172 HTDir *dir = HTDir_new(request, dir_show, dir_key);
173 char datestr[20];
174 char sizestr[10];
175 HTFileMode mode;
176 #ifdef HT_REENTRANT
177 struct dirent result; /* For readdir_r */
178 #endif
179
180 #ifdef HAVE_READDIR_R_2
181 while ((dirbuf = (struct dirent *) readdir_r(dp, &result)))
182 #elif defined(HAVE_READDIR_R_3)
183 while (readdir_r(dp, &result, &dirbuf) == 0)
184 #else
185 while ((dirbuf = readdir(dp)))
186 #endif /* HAVE_READDIR_R_2 */
187 {
188 /* Current and parent directories are never shown in list */
189 #ifdef HAVE_DIRENT_INO
190 if (!dirbuf->d_ino ||
191 !strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, ".."))
192 #else
193 if (!strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, ".."))
194 #endif
195 continue;
196
197 /* Make a lstat on the file */
198 strcpy(name, dirbuf->d_name);
199 if (HT_LSTAT(fullname, &file_info)) {
200 HTTRACE(PROT_TRACE, "Read dir.... lstat failed: %s\n" _ fullname);
201 continue;
202 }
203
204 /* Convert stat info to fit our setup */
205 if (((mode_t) file_info.st_mode & S_IFMT) == S_IFDIR) {
206 #ifdef VMS
207 char *dot = strstr(name, ".DIR"); /* strip .DIR part... */
208 if (dot) *dot = '\0';
209 #endif /* VMS */
210 mode = HT_IS_DIR;
211 if (dir_show & HT_DS_SIZE) strcpy(sizestr, "-");
212 } else {
213 mode = HT_IS_FILE;
214 if (dir_show & HT_DS_SIZE)
215 HTNumToStr(file_info.st_size, sizestr, 10);
216 }
217 if (dir_show & HT_DS_DATE)
218 HTDateDirStr(&file_info.st_mtime, datestr, 20);
219
220 /* Add to the list */
221 if (HTDir_addElement(dir, name, datestr, sizestr, mode) != YES)
222 break;
223 }
224 closedir(dp);
225 HTDir_free(dir);
226 return HT_LOADED;
227 } else {
228 HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "opendir");
229 return HT_ERROR;
230 }
231 #else
232 return HT_ERROR; /* needed for WWW_MSWINDOWS */
233 #endif /* HAVE_READDIR */
234 }
235
236 /* Determine write access to a file
237 ** --------------------------------
238 ** If stat_info is NULL then the function calls stat() on it's own,
239 ** otherwise it uses the information found in stat_info
240 ** On exit,
241 ** return value YES if file can be accessed and can be written to.
242 **
243 ** Bugs:
244 ** 1. No code for non-unix systems.
245 ** 2. Isn't there a quicker way?
246 */
HTEditable(const char * filename,struct stat * stat_info)247 PRIVATE BOOL HTEditable (const char * filename, struct stat * stat_info)
248 {
249 #ifdef GETGROUPS_T
250 int i;
251 uid_t myUid;
252 int ngroups; /* The number of groups */
253 struct stat fileStatus;
254 struct stat *fileptr = stat_info ? stat_info : &fileStatus;
255 GETGROUPS_T groups[NGROUPS];
256 if (!stat_info) {
257 if (HT_STAT(filename, &fileStatus))
258 return NO; /* Can't even access file! */
259 }
260 ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */
261 myUid = geteuid(); /* Get my user identifier */
262
263 #ifdef HTDEBUG
264 if (PROT_TRACE) {
265 int i;
266 HTTRACE(PROT_TRACE,
267 "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (" _
268 (unsigned int) fileptr->st_mode _
269 (int) fileptr->st_uid _ (int) fileptr->st_gid _
270 (int) myUid _ ngroups);
271 for (i=0; i<ngroups; i++) HTTRACE(PROT_TRACE, " %d" _ (int) groups[i]);
272 HTTRACE(PROT_TRACE, ")\n");
273 }
274 #endif /* HTDEBUG */
275
276 if (fileptr->st_mode & 0002) /* I can write anyway? */
277 return YES;
278
279 if ((fileptr->st_mode & 0200) /* I can write my own file? */
280 && (fileptr->st_uid == myUid))
281 return YES;
282
283 if (fileptr->st_mode & 0020) /* Group I am in can write? */
284 {
285 for (i=0; i<ngroups; i++) {
286 if (groups[i] == fileptr->st_gid)
287 return YES;
288 }
289 }
290 HTTRACE(PROT_TRACE, "\tFile is not editable.\n");
291 return NO; /* If no excuse, can't do */
292 #else
293 /*
294 ** We don't know and can't find out. Can we be sure that when opening
295 ** a file in mode "a" that the file is not modified?
296 */
297 return NO;
298 #endif /* GETGROUPS_T */
299 }
300
301 /* FileCleanup
302 ** -----------
303 ** This function closes the connection and frees memory.
304 ** Returns YES on OK, else NO
305 */
FileCleanup(HTRequest * req,int status)306 PRIVATE int FileCleanup (HTRequest *req, int status)
307 {
308 HTNet * net = HTRequest_net(req);
309 file_info * file = (file_info *) HTNet_context(net);
310 HTStream * input = HTRequest_inputStream(req);
311
312 /* Free stream with data TO Local file system */
313 if (input) {
314 if (status == HT_INTERRUPTED)
315 (*input->isa->abort)(input, NULL);
316 else
317 (*input->isa->_free)(input);
318 HTRequest_setInputStream(req, NULL);
319 }
320
321 /*
322 ** Remove if we have registered a timer function as a callback
323 */
324 if (file->timer) {
325 HTTimer_delete(file->timer);
326 file->timer = NULL;
327 }
328
329 if (file) {
330 HT_FREE(file->local);
331 HT_FREE(file);
332 }
333 HTNet_delete(net, status);
334 return YES;
335 }
336
337
338 /* Load a document
339 ** ---------------
340 **
341 ** On entry,
342 ** request This is the request structure
343 ** On exit,
344 ** returns HT_ERROR Error has occured in call back
345 ** HT_OK Call back was OK
346 */
347 PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type);
348
HTLoadFile(SOCKET soc,HTRequest * request)349 PUBLIC int HTLoadFile (SOCKET soc, HTRequest * request)
350 {
351 file_info *file; /* Specific access information */
352 HTNet * net = HTRequest_net(request);
353 HTParentAnchor * anchor = HTRequest_anchor(request);
354
355 HTTRACE(PROT_TRACE, "HTLoadFile.. Looking for `%s\'\n" _
356 HTAnchor_physical(anchor));
357 if ((file = (file_info *) HT_CALLOC(1, sizeof(file_info))) == NULL)
358 HT_OUTOFMEM("HTLoadFILE");
359 file->state = FS_BEGIN;
360 file->net = net;
361 HTNet_setContext(net, file);
362 HTNet_setEventCallback(net, FileEvent);
363 HTNet_setEventParam(net, file); /* callbacks get http* */
364
365 return FileEvent(soc, file, HTEvent_BEGIN); /* get it started - ops is ignored */
366 }
367
ReturnEvent(HTTimer * timer,void * param,HTEventType type)368 PRIVATE int ReturnEvent (HTTimer * timer, void * param, HTEventType type)
369 {
370 file_info * file = (file_info *) param;
371 if (timer != file->timer)
372 HTDEBUGBREAK("File timer %p not in sync\n" _ timer);
373 HTTRACE(PROT_TRACE, "HTLoadFile.. Continuing %p with timer %p\n" _ file _ timer);
374
375 /*
376 ** Delete the timer
377 */
378 HTTimer_delete(file->timer);
379 file->timer = NULL;
380
381 /*
382 ** Now call the event again
383 */
384 return FileEvent(INVSOC, file, HTEvent_READ);
385 }
386
387
FileEvent(SOCKET soc,void * pVoid,HTEventType type)388 PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type)
389 {
390 file_info *file = pVoid; /* Specific access information */
391 int status = HT_ERROR;
392 HTNet * net = file->net;
393 HTRequest * request = HTNet_request(net);
394 HTParentAnchor * anchor = HTRequest_anchor(request);
395
396 /*
397 ** Initiate a new file structure and bind to request structure
398 ** This is actually state FILE_BEGIN, but it can't be in the state
399 ** machine as we need the structure first.
400 */
401 if (type == HTEvent_CLOSE) { /* Interrupted */
402 HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED,
403 NULL, 0, "HTLoadFile");
404 FileCleanup(request, HT_INTERRUPTED);
405 return HT_OK;
406 }
407
408
409 /* Now jump into the machine. We know the state from the previous run */
410 while (1) {
411 switch (file->state) {
412 case FS_BEGIN:
413
414 /* We only support safe (GET, HEAD, etc) methods for the moment */
415 if (!HTMethod_isSafe(HTRequest_method(request))) {
416 HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_ALLOWED,
417 NULL, 0, "HTLoadFile");
418 file->state = FS_ERROR;
419 break;
420 }
421
422 /* Check whether we have access to local disk at all */
423 if (HTLib_secure()) {
424 HTTRACE(PROT_TRACE, "LoadFile.... No access to local file system\n");
425 file->state = FS_TRY_FTP;
426 break;
427 }
428 file->local = HTWWWToLocal(HTAnchor_physical(anchor), "",
429 HTRequest_userProfile(request));
430 if (!file->local) {
431 file->state = FS_TRY_FTP;
432 break;
433 }
434
435 /* Create a new host object and link it to the net object */
436 {
437 HTHost * host = NULL;
438 if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR;
439 HTNet_setHost(net, host);
440 if (HTHost_addNet(host, net) == HT_PENDING) {
441 HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n");
442 /* move to the hack state */
443 file->state = FS_PENDING;
444 return HT_OK;
445 }
446 }
447 file->state = FS_DO_CN;
448 break;
449
450 case FS_PENDING:
451 /*
452 ** 2000/08/10 JK : This is a funny state. Because of the
453 ** internal libwww stacks, when doing multiple local
454 ** requests (e.g., while using the Robot), we need to ask
455 ** again for the host object. If we had jumped directly to
456 ** the FS_DO_CN state, libwww would have blocked because
457 ** of socket starvation.
458 ** This state is similar to FS_BEGINNING, but just requests
459 ** the host object.
460 ** YES. THIS IS AN UGLY HACK!!
461 */
462 {
463 HTHost * host = NULL;
464 if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR;
465 HTNet_setHost(net, host);
466 if (HTHost_addNet(host, net) == HT_PENDING) {
467 HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n");
468 file->state = FS_PENDING;
469 return HT_OK;
470 }
471 }
472 file->state = FS_DO_CN;
473 break;
474
475 case FS_DO_CN:
476 /*
477 ** If we have to do content negotiation then find the object that
478 ** fits best into either what the client has indicated in the
479 ** accept headers or what the client has registered on its own.
480 ** The object chosen can in fact be a directory! However, content
481 ** negotiation only makes sense if we can read the directory!
482 ** We stat the file in order to find the size and to see it if
483 ** exists.
484 */
485 if (HTRequest_negotiation(request) &&
486 HTMethod_isSafe(HTRequest_method(request))) {
487 char * conneg = HTMulti(request, file->local,&file->stat_info);
488 if (conneg) {
489 HT_FREE(file->local);
490 file->local = conneg;
491 HTAnchor_setPhysical(anchor, conneg);
492 HTTRACE(PROT_TRACE, "Load File... Found `%s\'\n" _ conneg);
493 } else {
494 HTTRACE(PROT_TRACE, "Load File... Not found - even tried content negotiation\n");
495 HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
496 NULL, 0, "HTLoadFile");
497 file->state = FS_ERROR;
498 break;
499 }
500 } else {
501 if (HT_STAT(file->local, &file->stat_info) == -1) {
502 HTTRACE(PROT_TRACE, "Load File... Not found `%s\'\n" _ file->local);
503 HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
504 NULL, 0, "HTLoadFile");
505 file->state = FS_ERROR;
506 break;
507 }
508 }
509
510 /*
511 ** Check to see if the 'localname' is in fact a directory.
512 ** Note that we can't do a HEAD on a directory
513 */
514 if (((file->stat_info.st_mode) & S_IFMT) == S_IFDIR) {
515 if (HTRequest_method(request) == METHOD_GET)
516 file->state = FS_PARSE_DIR;
517 else {
518 HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT,
519 NULL, 0, "HTLoadFile");
520 file->state = FS_NO_DATA;
521 }
522 break;
523 }
524
525 /*
526 ** If empty file then only serve it if it is editable. We also get
527 ** the bindings for the file suffixes in lack of better bindings
528 */
529 {
530 BOOL editable = HTEditable(file->local, &file->stat_info);
531 if (file_suffix_binding) HTBind_getAnchorBindings(anchor);
532 if (editable) HTAnchor_appendAllow(anchor, METHOD_PUT);
533
534 /* Set the file size */
535 if (file->stat_info.st_size)
536 HTAnchor_setLength(anchor, file->stat_info.st_size);
537
538 /* Set the file last modified time stamp */
539 if (file->stat_info.st_mtime > 0)
540 HTAnchor_setLastModified(anchor, file->stat_info.st_mtime);
541
542 /* Check to see if we can edit it */
543 if (!editable && !file->stat_info.st_size) {
544 HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT,
545 NULL, 0, "HTLoadFile");
546 file->state = FS_NO_DATA;
547 } else {
548 file->state = (HTRequest_method(request)==METHOD_GET) ?
549 FS_NEED_OPEN_FILE : FS_GOT_DATA;
550 }
551 }
552 break;
553
554 case FS_NEED_OPEN_FILE:
555 status = HTFileOpen(net, file->local, HT_FB_RDONLY);
556 if (status == HT_OK) {
557 /*
558 ** Create the stream pipe FROM the channel to the application.
559 ** The target for the input stream pipe is set up using the
560 ** stream stack.
561 */
562 {
563 HTStream * rstream = HTStreamStack(HTAnchor_format(anchor),
564 HTRequest_outputFormat(request),
565 HTRequest_outputStream(request),
566 request, YES);
567 HTNet_setReadStream(net, rstream);
568 HTRequest_setOutputConnected(request, YES);
569 }
570
571 /*
572 ** Create the stream pipe TO the channel from the application
573 ** and hook it up to the request object
574 */
575 {
576 HTOutputStream * output = HTNet_getOutput(net, NULL, 0);
577 HTRequest_setInputStream(request, (HTStream *) output);
578 }
579
580 /*
581 ** Set up concurrent read/write if this request isn't the
582 ** source for a PUT or POST. As source we don't start reading
583 ** before all destinations are ready. If destination then
584 ** register the input stream and get ready for read
585 */
586 if (HTRequest_isSource(request) && !HTRequest_destinationsReady(request))
587 return HT_OK;
588 HTRequest_addError(request, ERR_INFO, NO, HTERR_OK, NULL, 0,
589 "HTLoadFile");
590 file->state = FS_NEED_BODY;
591
592 /* If we are _not_ using preemptive mode and we are Unix fd's
593 ** then return here to get the same effect as when we are
594 ** connecting to a socket. That way, HTFile acts just like any
595 ** other protocol module even though we are in fact doing
596 ** blocking connect
597 */
598 if (HTEvent_isCallbacksRegistered()) {
599 if (!HTRequest_preemptive(request)) {
600 if (!HTNet_preemptive(net)) {
601 HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n");
602 HTHost_register(HTNet_host(net), net, HTEvent_READ);
603 } else if (!file->timer) {
604 HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n");
605 file->timer =
606 HTTimer_new(NULL, ReturnEvent, file, 1, YES, NO);
607 }
608 return HT_OK;
609 }
610 }
611 } else if (status == HT_WOULD_BLOCK || status == HT_PENDING)
612 return HT_OK;
613 else {
614 HTRequest_addError(request, ERR_INFO, NO, HTERR_INTERNAL,
615 NULL, 0, "HTLoadFile");
616 file->state = FS_ERROR; /* Error or interrupt */
617 }
618 break;
619
620 case FS_NEED_BODY:
621 status = HTHost_read(HTNet_host(net), net);
622 if (status == HT_WOULD_BLOCK)
623 return HT_OK;
624 else if (status == HT_LOADED || status == HT_CLOSED) {
625 file->state = FS_GOT_DATA;
626 } else {
627 HTRequest_addError(request, ERR_INFO, NO, HTERR_FORBIDDEN,
628 NULL, 0, "HTLoadFile");
629 file->state = FS_ERROR;
630 }
631 break;
632
633 case FS_PARSE_DIR:
634 status = HTFile_readDir(request, file);
635 if (status == HT_LOADED)
636 file->state = FS_GOT_DATA;
637 else
638 file->state = FS_ERROR;
639 break;
640
641 case FS_TRY_FTP:
642 {
643 char *url = HTAnchor_physical(anchor);
644 HTAnchor *anchor;
645 char *newname = NULL;
646 StrAllocCopy(newname, "ftp:");
647 if (!strncmp(url, "file:", 5))
648 StrAllocCat(newname, url+5);
649 else
650 StrAllocCat(newname, url);
651 anchor = HTAnchor_findAddress(newname);
652 HTRequest_setAnchor(request, anchor);
653 HT_FREE(newname);
654 FileCleanup(request, HT_IGNORE);
655 return HTLoad(request, YES);
656 }
657 break;
658
659 case FS_GOT_DATA:
660 FileCleanup(request, HT_LOADED);
661 return HT_OK;
662 break;
663
664 case FS_NO_DATA:
665 FileCleanup(request, HT_NO_DATA);
666 return HT_OK;
667 break;
668
669 case FS_RETRY:
670 FileCleanup(request, HT_RETRY);
671 return HT_OK;
672 break;
673
674 case FS_ERROR:
675 FileCleanup(request, HT_ERROR);
676 return HT_OK;
677 break;
678 }
679 } /* End of while(1) */
680 }
681
682
683 /* Calculate the required buffer size (in bytes) for directory
684 ** entries read from the given directory handle. Return -1 if this
685 ** this cannot be done.
686 */
HTFile_dirent_buf_size(DIR * dirp)687 PUBLIC size_t HTFile_dirent_buf_size(DIR * dirp)
688 {
689 long name_max;
690 #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
691 name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
692 if (name_max == -1)
693 #if defined(NAME_MAX)
694 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
695 #else
696 return (size_t)(-1);
697 #endif
698 #else
699 #if defined(NAME_MAX)
700 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
701 #else
702 #error "buffer size for readdir_r cannot be determined"
703 #endif
704 #endif
705 return (size_t)offsetof(struct dirent, d_name) + name_max + 1;
706 }
707