source: rtems/cpukit/libcsupport/include/rtems/libio_.h @ 847ad44

4.11
Last change on this file since 847ad44 was 847ad44, checked in by Sebastian Huber <sebastian.huber@…>, on May 23, 2012 at 9:39:50 AM

Filesystem: Wait for unmount() to finish

  • Property mode set to 100644
File size: 20.5 KB
Line 
1/**
2 * @file rtems/libio_.h
3 *
4 * This file is the libio internal interface.
5 */
6
7/*
8 *  COPYRIGHT (c) 1989-2011.
9 *  On-Line Applications Research Corporation (OAR).
10 *
11 *  Modifications to support reference counting in the file system are
12 *  Copyright (c) 2012 embedded brains GmbH.
13 *
14 *  The license and distribution terms for this file may be
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.com/license/LICENSE.
17 */
18
19#ifndef _RTEMS_RTEMS_LIBIO__H
20#define _RTEMS_RTEMS_LIBIO__H
21
22#include <errno.h>
23
24#include <rtems.h>
25#include <rtems/libio.h>
26#include <rtems/seterr.h>
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32#define RTEMS_FILESYSTEM_SYMLOOP_MAX 32
33
34/*
35 *  Semaphore to protect the io table
36 */
37
38#define RTEMS_LIBIO_SEM         rtems_build_name('L', 'B', 'I', 'O')
39#define RTEMS_LIBIO_IOP_SEM(n)  rtems_build_name('L', 'B', 'I', n)
40
41/**
42 * @brief Event to signal an unmount process completion.
43 *
44 * This event should equal the RTEMS_BDBUF_TRANSFER_SYNC event to avoid too
45 * many events reserved for the file system.
46 *
47 * @see rtems_filesystem_do_unmount() and unmount().
48 */
49#define RTEMS_FILESYSTEM_UNMOUNT_EVENT RTEMS_EVENT_1
50
51extern rtems_id                          rtems_libio_semaphore;
52
53/*
54 *  File descriptor Table Information
55 */
56
57extern uint32_t        rtems_libio_number_iops;
58extern rtems_libio_t  *rtems_libio_iops;
59extern rtems_libio_t  *rtems_libio_last_iop;
60extern rtems_libio_t *rtems_libio_iop_freelist;
61
62extern const rtems_filesystem_file_handlers_r rtems_filesystem_null_handlers;
63
64extern rtems_filesystem_mount_table_entry_t rtems_filesystem_null_mt_entry;
65
66/**
67 * @brief The global null location.
68 *
69 * Every operation and the open and fstat handlers of this location returns an
70 * error status.  The errno is not touched by these operations and handlers.
71 * The purpose of this location is to deliver the error return status for a
72 * previous error condition which must set the errno accordingly.
73 *
74 * The usage of this null location instead of the NULL pointer eliminates a lot
75 * of branches.
76 *
77 * The user environment root and current directory are statically initialized
78 * with the null location.  Due to that all file system services are in a
79 * defined state even if no root file system was mounted.
80 */
81extern rtems_filesystem_global_location_t rtems_filesystem_global_location_null;
82
83/*
84 *  rtems_libio_iop
85 *
86 *  Macro to return the file descriptor pointer.
87 */
88
89#define rtems_libio_iop(_fd) \
90  ((((uint32_t)(_fd)) < rtems_libio_number_iops) ? \
91         &rtems_libio_iops[_fd] : 0)
92
93/*
94 *  rtems_libio_iop_to_descriptor
95 *
96 *  Macro to convert an internal file descriptor pointer (iop) into
97 *  the integer file descriptor used by the "section 2" system calls.
98 */
99
100#define rtems_libio_iop_to_descriptor(_iop) \
101   ((!(_iop)) ? -1 : (_iop - rtems_libio_iops))
102
103/*
104 *  rtems_libio_check_is_open
105 *
106 *  Macro to check if a file descriptor is actually open.
107 */
108
109#define rtems_libio_check_is_open(_iop) \
110  do {                                               \
111      if (((_iop)->flags & LIBIO_FLAGS_OPEN) == 0) { \
112          errno = EBADF;                             \
113          return -1;                                 \
114      }                                              \
115  } while (0)
116
117/*
118 *  rtems_libio_check_fd
119 *
120 *  Macro to check if a file descriptor number is valid.
121 */
122
123#define rtems_libio_check_fd(_fd) \
124  do {                                                     \
125      if ((uint32_t) (_fd) >= rtems_libio_number_iops) {   \
126          errno = EBADF;                                   \
127          return -1;                                       \
128      }                                                    \
129  } while (0)
130
131/*
132 *  rtems_libio_check_buffer
133 *
134 *  Macro to check if a buffer pointer is valid.
135 */
136
137#define rtems_libio_check_buffer(_buffer) \
138  do {                                    \
139      if ((_buffer) == 0) {               \
140          errno = EINVAL;                 \
141          return -1;                      \
142      }                                   \
143  } while (0)
144
145/*
146 *  rtems_libio_check_count
147 *
148 *  Macro to check if a count or length is valid.
149 */
150
151#define rtems_libio_check_count(_count) \
152  do {                                  \
153      if ((_count) == 0) {              \
154          return 0;                     \
155      }                                 \
156  } while (0)
157
158/*
159 *  rtems_libio_check_permissions_with_error
160 *
161 *  Macro to check if a file descriptor is open for this operation.
162 *  On failure, return the user specified error.
163 */
164
165#define rtems_libio_check_permissions_with_error(_iop, _flag, _errno) \
166  do {                                                      \
167      if (((_iop)->flags & (_flag)) == 0) {                 \
168            rtems_set_errno_and_return_minus_one( _errno ); \
169            return -1;                                      \
170      }                                                     \
171  } while (0)
172
173/*
174 *  rtems_libio_check_permissions
175 *
176 *  Macro to check if a file descriptor is open for this operation.
177 *  On failure, return EINVAL
178 */
179
180#define rtems_libio_check_permissions(_iop, _flag) \
181   rtems_libio_check_permissions_with_error(_iop, _flag, EINVAL )
182
183/**
184 * @brief Clones a node.
185 *
186 * The caller must hold the file system instance lock.
187 *
188 * @param[out] clone The cloned location.
189 * @param[in] master The master location.
190 *
191 * @see rtems_filesystem_instance_lock().
192 */
193void rtems_filesystem_location_clone(
194  rtems_filesystem_location_info_t *clone,
195  const rtems_filesystem_location_info_t *master
196);
197
198/**
199 * @brief Returns the type of a node.
200 *
201 * This function obtains and releases the file system instance lock.
202 *
203 * @param[in] loc The location of the node.
204 *
205 * @return The node type.
206 *
207 * @see rtems_filesystem_instance_lock().
208 */
209rtems_filesystem_node_types_t rtems_filesystem_node_type(
210  const rtems_filesystem_location_info_t *loc
211);
212
213/**
214 * @brief Releases all resources of a location.
215 *
216 * This function may block on a mutex and may complete an unmount process.
217 *
218 * @param[in] loc The location to free.
219 *
220 * @note The file system root location is released by the file system instance
221 * destruction handler (see @ref rtems_filesystem_fsunmount_me_t).
222 *
223 * @see rtems_filesystem_freenode_t.
224 */
225void rtems_filesystem_location_free( rtems_filesystem_location_info_t *loc );
226
227/*
228 *  External structures
229 */
230#include <rtems/userenv.h>
231
232static inline void rtems_libio_lock( void )
233{
234  rtems_semaphore_obtain( rtems_libio_semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT );
235}
236
237static inline void rtems_libio_unlock( void )
238{
239  rtems_semaphore_release( rtems_libio_semaphore );
240}
241
242static inline void rtems_filesystem_mt_lock( void )
243{
244  rtems_libio_lock();
245}
246
247static inline void rtems_filesystem_mt_unlock( void )
248{
249  rtems_libio_unlock();
250}
251
252#define rtems_filesystem_mt_entry_declare_lock_context( ctx ) \
253  rtems_interrupt_level ctx
254
255#define rtems_filesystem_mt_entry_lock( ctx ) rtems_interrupt_disable( ctx )
256
257#define rtems_filesystem_mt_entry_unlock( ctx ) rtems_interrupt_enable( ctx )
258
259static inline void rtems_filesystem_instance_lock(
260  const rtems_filesystem_location_info_t *loc
261)
262{
263  const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
264
265  (*mt_entry->ops->lock_h)( mt_entry );
266}
267
268static inline void rtems_filesystem_instance_unlock(
269  const rtems_filesystem_location_info_t *loc
270)
271{
272  const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
273
274  (*mt_entry->ops->unlock_h)( mt_entry );
275}
276
277/*
278 *  File Descriptor Routine Prototypes
279 */
280
281rtems_libio_t *rtems_libio_allocate(void);
282
283uint32_t rtems_libio_fcntl_flags( int fcntl_flags );
284
285int rtems_libio_to_fcntl_flags( uint32_t flags );
286
287void rtems_libio_free(
288  rtems_libio_t *iop
289);
290
291/*
292 *  File System Routine Prototypes
293 */
294
295rtems_filesystem_location_info_t *
296rtems_filesystem_eval_path_start(
297  rtems_filesystem_eval_path_context_t *ctx,
298  const char *path,
299  int eval_flags
300);
301
302rtems_filesystem_location_info_t *
303rtems_filesystem_eval_path_start_with_parent(
304  rtems_filesystem_eval_path_context_t *ctx,
305  const char *path,
306  int eval_flags,
307  rtems_filesystem_location_info_t *parentloc,
308  int parent_eval_flags
309);
310
311rtems_filesystem_location_info_t *
312rtems_filesystem_eval_path_start_with_root_and_current(
313  rtems_filesystem_eval_path_context_t *ctx,
314  const char *path,
315  int eval_flags,
316  rtems_filesystem_global_location_t *const *global_root_ptr,
317  rtems_filesystem_global_location_t *const *global_current_ptr
318);
319
320void rtems_filesystem_eval_path_continue(
321  rtems_filesystem_eval_path_context_t *ctx
322);
323
324void rtems_filesystem_eval_path_cleanup(
325  rtems_filesystem_eval_path_context_t *ctx
326);
327
328void rtems_filesystem_eval_path_recursive(
329  rtems_filesystem_eval_path_context_t *ctx,
330  const char *path,
331  size_t pathlen
332);
333
334void rtems_filesystem_eval_path_cleanup_with_parent(
335  rtems_filesystem_eval_path_context_t *ctx,
336  rtems_filesystem_location_info_t *parentloc
337);
338
339/**
340 * @brief Requests a path evaluation restart.
341 *
342 * Sets the start and current location to the new start location.  The caller
343 * must terminate its current evaluation process.  The path evaluation
344 * continues in the next loop iteration within
345 * rtems_filesystem_eval_path_continue().  This avoids recursive invokations.
346 * The function obtains the new start location and clones it to set the new
347 * current location.  The previous start and current locations are released.
348 *
349 * @param[in, out] ctx The path evaluation context.
350 * @param[in, out] newstartloc_ptr Pointer to new start location.
351 */
352void rtems_filesystem_eval_path_restart(
353  rtems_filesystem_eval_path_context_t *ctx,
354  rtems_filesystem_global_location_t **newstartloc_ptr
355);
356
357typedef enum {
358  RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE,
359  RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE,
360  RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY
361} rtems_filesystem_eval_path_generic_status;
362
363/**
364 * @brief Tests if the current location is a directory.
365 *
366 * @param[in, out] ctx The path evaluation context.
367 * @param[in, out] arg The handler argument.
368 *
369 * @retval true The current location is a directory.
370 * @retval false Otherwise.
371 *
372 * @see rtems_filesystem_eval_path_generic().
373 */
374typedef bool (*rtems_filesystem_eval_path_is_directory)(
375  rtems_filesystem_eval_path_context_t *ctx,
376  void *arg
377);
378
379/**
380 * @brief Evaluates a token.
381 *
382 * @param[in, out] ctx The path evaluation context.
383 * @param[in, out] arg The handler argument.
384 * @param[in] token The token contents.
385 * @param[in] tokenlen The token length in characters.
386 *
387 * @retval status The generic path evaluation status.
388 *
389 * @see rtems_filesystem_eval_path_generic().
390 */
391typedef rtems_filesystem_eval_path_generic_status
392(*rtems_filesystem_eval_path_eval_token)(
393  rtems_filesystem_eval_path_context_t *ctx,
394  void *arg,
395  const char *token,
396  size_t tokenlen
397);
398
399typedef struct {
400  rtems_filesystem_eval_path_is_directory is_directory;
401  rtems_filesystem_eval_path_eval_token eval_token;
402} rtems_filesystem_eval_path_generic_config;
403
404void rtems_filesystem_eval_path_generic(
405  rtems_filesystem_eval_path_context_t *ctx,
406  void *arg,
407  const rtems_filesystem_eval_path_generic_config *config
408);
409
410void rtems_filesystem_initialize(void);
411
412/**
413 * @brief Copies a location.
414 *
415 * A bitwise copy is performed.  The destination location will be added to the
416 * corresponding mount entry.
417 *
418 * @param[out] dst The destination location.
419 * @param[in] src The source location.
420 *
421 * @retval dst The destination location.
422 *
423 * @see rtems_filesystem_location_clone().
424 */
425rtems_filesystem_location_info_t *rtems_filesystem_location_copy(
426  rtems_filesystem_location_info_t *dst,
427  const rtems_filesystem_location_info_t *src
428);
429
430static inline rtems_filesystem_location_info_t *
431rtems_filesystem_location_initialize_to_null(
432  rtems_filesystem_location_info_t *loc
433)
434{
435  return rtems_filesystem_location_copy(
436    loc,
437    &rtems_filesystem_global_location_null.location
438  );
439}
440
441rtems_filesystem_global_location_t *
442rtems_filesystem_location_transform_to_global(
443  rtems_filesystem_location_info_t *loc
444);
445
446/**
447 * @brief Assigns a global file system location.
448 *
449 * @param[in, out] lhs_global_loc_ptr Pointer to the global left hand side file
450 * system location.  The current left hand side location will be released.
451 * @param[in] rhs_global_loc The global right hand side file system location.
452 */
453void rtems_filesystem_global_location_assign(
454  rtems_filesystem_global_location_t **lhs_global_loc_ptr,
455  rtems_filesystem_global_location_t *rhs_global_loc
456);
457
458/**
459 * @brief Obtains a global file system location.
460 *
461 * Deferred releases will be processed in this function.
462 *
463 * This function must be called from normal thread context and may block on a
464 * mutex.  Thread dispatching is disabled to protect some critical sections.
465 *
466 * @param[in] global_loc_ptr Pointer to the global file system location.
467 *
468 * @return A global file system location.  It returns always a valid object.
469 * In case of an error, the global null location will be returned.  Each
470 * operation or handler of the null location returns an error status.  The
471 * errno indicates the error.  The NULL pointer is never returned.
472 *
473 * @see rtems_filesystem_location_transform_to_global(),
474 * rtems_filesystem_global_location_obtain_null(), and
475 * rtems_filesystem_global_location_release().
476 */
477rtems_filesystem_global_location_t *rtems_filesystem_global_location_obtain(
478  rtems_filesystem_global_location_t *const *global_loc_ptr
479);
480
481/**
482 * @brief Releases a global file system location.
483 *
484 * In case the reference count reaches zero, all associated resources will be
485 * released.  This may include the complete unmount of the corresponding file
486 * system instance.
487 *
488 * This function may block on a mutex.  It may be called within critical
489 * sections of the operating system.  In this case the release will be
490 * deferred.  The next obtain call will do the actual release.
491 *
492 * @param[in] global_loc The global file system location.  It must not be NULL.
493 *
494 * @see rtems_filesystem_global_location_obtain().
495 */
496void rtems_filesystem_global_location_release(
497  rtems_filesystem_global_location_t *global_loc
498);
499
500void rtems_filesystem_location_detach(
501  rtems_filesystem_location_info_t *detach
502);
503
504void rtems_filesystem_location_copy_and_detach(
505  rtems_filesystem_location_info_t *copy,
506  rtems_filesystem_location_info_t *detach
507);
508
509static inline rtems_filesystem_global_location_t *
510rtems_filesystem_global_location_obtain_null(void)
511{
512  rtems_filesystem_global_location_t *global_loc = NULL;
513
514  return rtems_filesystem_global_location_obtain( &global_loc );
515}
516
517static inline bool rtems_filesystem_location_is_null(
518  const rtems_filesystem_location_info_t *loc
519)
520{
521  return loc->handlers == &rtems_filesystem_null_handlers;
522}
523
524static inline bool rtems_filesystem_global_location_is_null(
525  const rtems_filesystem_global_location_t *global_loc
526)
527{
528  return rtems_filesystem_location_is_null( &global_loc->location );
529}
530
531static inline void rtems_filesystem_location_error(
532  const rtems_filesystem_location_info_t *loc,
533  int eno
534)
535{
536  if ( !rtems_filesystem_location_is_null( loc ) ) {
537    errno = eno;
538  }
539}
540
541int rtems_filesystem_mknod(
542  const rtems_filesystem_location_info_t *parentloc,
543  const char *name,
544  size_t namelen,
545  mode_t mode,
546  dev_t dev
547);
548
549int rtems_filesystem_chdir( rtems_filesystem_location_info_t *loc );
550
551int rtems_filesystem_chown(
552  const char *path,
553  uid_t owner,
554  gid_t group,
555  int eval_follow_link
556);
557
558static inline bool rtems_filesystem_is_ready_for_unmount(
559  rtems_filesystem_mount_table_entry_t *mt_entry
560)
561{
562  bool ready = !mt_entry->mounted
563    && rtems_chain_has_only_one_node( &mt_entry->location_chain )
564    && mt_entry->mt_fs_root->reference_count == 1;
565
566  if ( ready ) {
567    rtems_chain_initialize_empty( &mt_entry->location_chain );
568  }
569
570  return ready;
571}
572
573static inline void rtems_filesystem_location_add_to_mt_entry(
574  rtems_filesystem_location_info_t *loc
575)
576{
577  rtems_filesystem_mt_entry_declare_lock_context( lock_context );
578
579  rtems_filesystem_mt_entry_lock( lock_context );
580  rtems_chain_append_unprotected(
581    &loc->mt_entry->location_chain,
582    &loc->mt_entry_node
583  );
584  rtems_filesystem_mt_entry_unlock( lock_context );
585}
586
587void rtems_filesystem_location_remove_from_mt_entry(
588  rtems_filesystem_location_info_t *loc
589);
590
591void rtems_filesystem_do_unmount(
592  rtems_filesystem_mount_table_entry_t *mt_entry
593);
594
595static inline bool rtems_filesystem_location_is_root(
596  const rtems_filesystem_location_info_t *loc
597)
598{
599  const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
600
601  return (*mt_entry->ops->are_nodes_equal_h)(
602    loc,
603    &mt_entry->mt_fs_root->location
604  );
605}
606
607static inline const char *rtems_filesystem_eval_path_get_path(
608  rtems_filesystem_eval_path_context_t *ctx
609)
610{
611  return ctx->path;
612}
613
614static inline size_t rtems_filesystem_eval_path_get_pathlen(
615  rtems_filesystem_eval_path_context_t *ctx
616)
617{
618  return ctx->pathlen;
619}
620
621static inline void rtems_filesystem_eval_path_set_path(
622  rtems_filesystem_eval_path_context_t *ctx,
623  const char *path,
624  size_t pathlen
625)
626{
627  ctx->path = path;
628  ctx->pathlen = pathlen;
629}
630
631static inline void rtems_filesystem_eval_path_clear_path(
632  rtems_filesystem_eval_path_context_t *ctx
633)
634{
635  ctx->pathlen = 0;
636}
637
638static inline const char *rtems_filesystem_eval_path_get_token(
639  rtems_filesystem_eval_path_context_t *ctx
640)
641{
642  return ctx->token;
643}
644
645static inline size_t rtems_filesystem_eval_path_get_tokenlen(
646  rtems_filesystem_eval_path_context_t *ctx
647)
648{
649  return ctx->tokenlen;
650}
651
652static inline void rtems_filesystem_eval_path_set_token(
653  rtems_filesystem_eval_path_context_t *ctx,
654  const char *token,
655  size_t tokenlen
656)
657{
658  ctx->token = token;
659  ctx->tokenlen = tokenlen;
660}
661
662static inline void rtems_filesystem_eval_path_clear_token(
663  rtems_filesystem_eval_path_context_t *ctx
664)
665{
666  ctx->tokenlen = 0;
667}
668
669static inline void rtems_filesystem_eval_path_put_back_token(
670  rtems_filesystem_eval_path_context_t *ctx
671)
672{
673  size_t tokenlen = ctx->tokenlen;
674
675  ctx->path -= tokenlen;
676  ctx->pathlen += tokenlen;
677  ctx->tokenlen = 0;
678}
679
680void rtems_filesystem_eval_path_eat_delimiter(
681  rtems_filesystem_eval_path_context_t *ctx
682);
683
684void rtems_filesystem_eval_path_next_token(
685  rtems_filesystem_eval_path_context_t *ctx
686);
687
688static inline void rtems_filesystem_eval_path_get_next_token(
689  rtems_filesystem_eval_path_context_t *ctx,
690  const char **token,
691  size_t *tokenlen
692)
693{
694  rtems_filesystem_eval_path_next_token(ctx);
695  *token = ctx->token;
696  *tokenlen = ctx->tokenlen;
697}
698
699static inline rtems_filesystem_location_info_t *
700rtems_filesystem_eval_path_get_currentloc(
701  rtems_filesystem_eval_path_context_t *ctx
702)
703{
704  return &ctx->currentloc;
705}
706
707static inline bool rtems_filesystem_eval_path_has_path(
708  const rtems_filesystem_eval_path_context_t *ctx
709)
710{
711  return ctx->pathlen > 0;
712}
713
714static inline bool rtems_filesystem_eval_path_has_token(
715  const rtems_filesystem_eval_path_context_t *ctx
716)
717{
718  return ctx->tokenlen > 0;
719}
720
721static inline int rtems_filesystem_eval_path_get_flags(
722  const rtems_filesystem_eval_path_context_t *ctx
723)
724{
725  return ctx->flags;
726}
727
728static inline void rtems_filesystem_eval_path_set_flags(
729  rtems_filesystem_eval_path_context_t *ctx,
730  int flags
731)
732{
733  ctx->flags = flags;
734}
735
736static inline void rtems_filesystem_eval_path_clear_and_set_flags(
737  rtems_filesystem_eval_path_context_t *ctx,
738  int clear,
739  int set
740)
741{
742  int flags = ctx->flags;
743
744  flags &= ~clear;
745  flags |= set;
746
747  ctx->flags = flags;
748}
749
750static inline void rtems_filesystem_eval_path_extract_currentloc(
751  rtems_filesystem_eval_path_context_t *ctx,
752  rtems_filesystem_location_info_t *get
753)
754{
755  rtems_filesystem_location_copy_and_detach(
756    get,
757    &ctx->currentloc
758  );
759}
760
761void rtems_filesystem_eval_path_error(
762  rtems_filesystem_eval_path_context_t *ctx,
763  int eno
764);
765
766/**
767 * @brief Checks that the locations exist in the same file system instance.
768 *
769 * @retval 0 The locations exist and are in the same file system instance.
770 * @retval -1 An error occured.  The @c errno indicates the error.
771 */
772int rtems_filesystem_location_exists_in_same_fs_instance_as(
773  const rtems_filesystem_location_info_t *a,
774  const rtems_filesystem_location_info_t *b
775);
776
777bool rtems_filesystem_check_access(
778  int eval_flags,
779  mode_t node_mode,
780  uid_t node_uid,
781  gid_t node_gid
782);
783
784bool rtems_filesystem_eval_path_check_access(
785  rtems_filesystem_eval_path_context_t *ctx,
786  int eval_flags,
787  mode_t node_mode,
788  uid_t node_uid,
789  gid_t node_gid
790);
791
792static inline bool rtems_filesystem_is_delimiter(char c)
793{
794  return c == '/' || c == '\\';
795}
796
797static inline bool rtems_filesystem_is_current_directory(
798  const char *token,
799  size_t tokenlen
800)
801{
802  return tokenlen == 1 && token [0] == '.';
803}
804
805static inline bool rtems_filesystem_is_parent_directory(
806  const char *token,
807  size_t tokenlen
808)
809{
810  return tokenlen == 2 && token [0] == '.' && token [1] == '.';
811}
812
813#ifdef __cplusplus
814}
815#endif
816
817#endif
818/* end of include file */
Note: See TracBrowser for help on using the repository browser.