4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
29 #include <sys/types.h>
35 #include <sys/modctl.h>
36 #include <sys/systeminfo.h>
41 #include <sys/sunddi.h>
42 #include <libdevinfo.h>
43 #include <sys/sysmacros.h>
51 * globals needed for libdevinfo - there is no way to pass
52 * private data to the find routine.
58 struct dev_list
*next
;
61 static char *kelf_desc
= NULL
;
62 static int kelf_type
= ELFCLASSNONE
;
65 static struct dev_list
*conflict_lst
= NULL
;
67 static int module_not_found(char *, char *, int, char **, int *);
69 static int update_minor_perm(char *, char *);
70 static int devfs_update_minor_perm(char *, char *);
71 static int update_driver_classes(char *, char *);
72 static int drv_name_conflict(di_node_t
);
73 static int devfs_node(di_node_t node
, void *arg
);
74 static int drv_name_match(char *, int, char *, char *);
75 static void print_drv_conflict_info(int);
76 static void check_dev_dir(int);
77 static int dev_node(const char *, const struct stat
*, int, struct FTW
*);
78 static void free_conflict_list(struct dev_list
*);
79 static int clone(di_node_t node
);
80 static int elf_type(char *, char **, int *);
81 static int correct_location(char *, char **, int *);
82 static int isaspec_drvmod_discovery();
83 static void remove_slashes(char *);
84 static int update_extra_privs(char *, char *privlist
);
85 static int ignore_root_basedir();
88 main(int argc
, char *argv
[])
92 char driver_name
[FILENAME_MAX
+ 1];
93 int driver_name_size
= sizeof (driver_name
);
94 char path_driver_name
[MAXPATHLEN
];
95 int path_driver_name_size
= sizeof (path_driver_name
);
102 int verbose_flag
= 0;
108 int cleanup_flag
= 0;
110 char *basedir
= NULL
;
114 di_node_t root_node
; /* for device tree snapshot */
115 char *drvelf_desc
= NULL
;
116 int drvelf_type
= ELFCLASSNONE
;
121 (void) setlocale(LC_ALL
, "");
122 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
123 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
125 (void) textdomain(TEXT_DOMAIN
);
127 /* must be run by root */
129 if (geteuid() != 0) {
130 (void) fprintf(stderr
, gettext(ERR_NOT_ROOT
));
134 while ((opt
= getopt(argc
, argv
, "vfm:ni:b:c:p:P:u")) != EOF
) {
152 if (check_space_within_quote(aliases
) == ERROR
) {
153 (void) fprintf(stderr
, gettext(ERR_NO_SPACE
),
161 if (strcmp(basedir
, "/") == 0 &&
162 ignore_root_basedir()) {
179 * Update binding files and kernel but
180 * do not load or configure devices.
192 if (argv
[optind
] != NULL
) {
193 if (strlcpy(driver_name
, argv
[optind
], driver_name_size
) >=
195 (void) fprintf(stderr
, gettext(ERR_DRVNAME_TOO_LONG
),
196 driver_name_size
, argv
[optind
]);
201 * check for extra args
203 if ((optind
+ 1) != argc
) {
213 if (getzoneid() != GLOBAL_ZONEID
) {
214 (void) fprintf(stderr
, gettext(ERR_NOT_GLOBAL_ZONE
));
219 * Fail if add_drv was invoked with a pathname prepended to the
220 * driver_name argument.
222 * Check driver_name for any '/'s. If found, we assume that caller
223 * is trying to specify a pathname.
226 slash
= strchr(driver_name
, '/');
228 remove_slashes(driver_name
);
230 /* extract module name out of path */
231 slash
= strrchr(driver_name
, '/');
234 (void) fprintf(stderr
, gettext(ERR_PATH_SPEC
),
236 (void) fprintf(stderr
, gettext(ERR_INSTALL_FAIL
),
241 new_drv
= driver_name
;
243 /* set up add_drv filenames */
244 if ((build_filenames(basedir
)) == ERROR
) {
248 /* must be only running version of add_drv/rem_drv */
251 if ((check_perms_aliases(m_flag
, i_flag
)) == ERROR
)
254 if ((check_name_to_major(R_OK
| W_OK
)) == ERROR
)
258 * check validity of options
261 if ((check_perm_opts(perms
)) == ERROR
) {
269 if ((aliases_unique(aliases
)) == ERROR
)
273 /* -u and -n/-b are mutually exclusive */
274 if (update_only
&& (noload_flag
|| server
)) {
279 /* update kernel unless -b or -n */
280 if (noload_flag
== 0 && server
== 0 &&
281 priv
!= NULL
&& check_priv_entry(priv
, 1) != 0)
284 if (policy
!= NULL
&&
285 (policy
= check_plcy_entry(policy
, driver_name
, B_FALSE
)) == NULL
) {
289 if ((unique_driver_name(driver_name
, name_to_major
,
290 &is_unique
)) == ERROR
)
293 if (is_unique
== NOT_UNIQUE
) {
294 (void) fprintf(stderr
, gettext(ERR_NOT_UNIQUE
), driver_name
);
298 if (noload_flag
== 0 && server
== 0) {
299 if (elf_type("/dev/ksyms", &kelf_desc
, &kelf_type
) == ERROR
) {
300 (void) fprintf(stderr
, gettext(ERR_KERNEL_ISA
));
304 if (module_not_found(driver_name
, path_driver_name
,
305 path_driver_name_size
, &drvelf_desc
, &drvelf_type
) ==
307 (void) fprintf(stderr
, gettext(ERR_NOMOD
), driver_name
);
312 * If the driver location is incorrect but the kernel and driver
313 * are of the same ISA, suggest a fix. If the driver location
314 * is incorrect and the ISA's mismatch, notify the user that
315 * this driver can not be loaded on this kernel. In both cases,
316 * do not attempt to load the driver module.
319 if (correct_location(path_driver_name
, &drvelf_desc
,
320 (&drvelf_type
)) == ERROR
) {
322 if (kelf_type
== drvelf_type
) {
323 (void) fprintf(stderr
,
324 gettext(ERR_SOL_LOCATION
), driver_name
,
327 (void) fprintf(stderr
,
328 gettext(ERR_NOT_LOADABLE
),
329 drvelf_desc
, driver_name
, kelf_desc
);
333 * The driver location is correct. Verify that the kernel ISA
334 * and driver ISA match. If they do not match, produce an error
335 * message and do not attempt to load the module.
338 } else if (kelf_type
!= drvelf_type
) {
340 (void) fprintf(stderr
, gettext(ERR_ISA_MISMATCH
),
341 kelf_desc
, driver_name
, drvelf_desc
);
342 (void) fprintf(stderr
, gettext(ERR_NOT_LOADABLE
),
343 drvelf_desc
, driver_name
, kelf_desc
);
348 * Check for a more specific driver conflict - see
350 * Note that drv_name_conflict() can return -1 for error
351 * or 1 for a conflict. Since the default is to fail unless
352 * the -f flag is specified, we don't bother to differentiate.
354 if ((root_node
= di_init("/", DINFOSUBTREE
| DINFOMINOR
))
356 (void) fprintf(stderr
, gettext(ERR_DEVTREE
));
359 conflict
= drv_name_conflict(root_node
);
365 * if the force flag is not set, we fail here
368 (void) fprintf(stderr
,
369 gettext(ERR_INSTALL_FAIL
), driver_name
);
370 (void) fprintf(stderr
, "Device managed by "
371 "another driver.\n");
373 print_drv_conflict_info(force_flag
);
377 * The force flag was specified so we print warnings
378 * and install the driver anyways
381 print_drv_conflict_info(force_flag
);
382 free_conflict_list(conflict_lst
);
386 if ((update_name_to_major(driver_name
, &major_num
, server
)) == ERROR
) {
390 cleanup_flag
|= CLEAN_NAM_MAJ
;
394 cleanup_flag
|= CLEAN_MINOR_PERM
;
395 if (update_minor_perm(driver_name
, perms
) == ERROR
) {
396 remove_entry(cleanup_flag
, driver_name
);
402 cleanup_flag
|= CLEAN_DRV_ALIAS
;
403 if (update_driver_aliases(driver_name
, aliases
) == ERROR
) {
404 remove_entry(cleanup_flag
, driver_name
);
411 cleanup_flag
|= CLEAN_DRV_CLASSES
;
412 if (update_driver_classes(driver_name
, classes
) == ERROR
) {
413 remove_entry(cleanup_flag
, driver_name
);
420 cleanup_flag
|= CLEAN_DRV_PRIV
;
421 if (update_extra_privs(driver_name
, priv
) == ERROR
) {
422 remove_entry(cleanup_flag
, driver_name
);
427 if (policy
!= NULL
) {
428 cleanup_flag
|= CLEAN_DEV_POLICY
;
429 if (update_device_policy(device_policy
, policy
, B_FALSE
)
431 remove_entry(cleanup_flag
, driver_name
);
436 if (noload_flag
|| server
) {
437 (void) fprintf(stderr
, gettext(BOOT_CLIENT
));
440 * paranoia - if we crash whilst configuring the driver
441 * this might avert possible file corruption.
447 config_flags
|= CONFIG_DRV_VERBOSE
;
449 config_flags
|= CONFIG_DRV_UPDATE_ONLY
;
451 if (config_driver(driver_name
, major_num
, aliases
, classes
,
452 cleanup_flag
, config_flags
) == ERROR
) {
456 if (devfs_update_minor_perm(basedir
,
457 driver_name
) == ERROR
) {
462 (void) fprintf(stderr
, gettext(INFO_UPDATE_ONLY
),
464 } else if (noload_flag
) {
465 (void) fprintf(stderr
, gettext(ERR_CONFIG_NOLOAD
),
468 load_driver(driver_name
, verbose_flag
);
472 if (create_reconfig(basedir
) == ERROR
)
473 (void) fprintf(stderr
, gettext(ERR_CREATE_RECONFIG
));
479 (void) fprintf(stderr
, gettext(DRIVER_INSTALLED
), driver_name
);
486 * Searches for the driver module along the module path (returned
487 * from modctl) and returns a string (in drv_path) representing the path
488 * where drv_name was found. ERROR is returned if function is unable
489 * to locate drv_name.
492 module_not_found(char *drv_name
, char *drv_path
, int drv_path_size
,
493 char **drvelf_desc
, int *drvelf_type_ptr
)
496 char data
[MAXMODPATHS
];
497 char pathsave
[MAXMODPATHS
];
499 struct drvmod_dir
*curdir
= NULL
;
500 char foundpath
[MAXPATHLEN
];
502 if (modctl(MODGETPATH
, NULL
, data
) != 0) {
503 (void) fprintf(stderr
, gettext(ERR_MODPATH
));
506 (void) strcpy(pathsave
, data
);
507 next
= strtok(data
, MOD_SEP
);
509 if (isaspec_drvmod_discovery() == ERROR
)
513 while (curdir
!= NULL
) {
514 while (next
!= NULL
) {
515 (void) snprintf(foundpath
, sizeof (foundpath
),
516 "%s/drv/%s/%s", next
, curdir
->direc
, drv_name
);
517 if ((stat(foundpath
, &buf
) == 0) &&
518 ((buf
.st_mode
& S_IFMT
) == S_IFREG
)) {
519 if (elf_type(foundpath
, drvelf_desc
,
520 drvelf_type_ptr
) == ERROR
) {
521 (void) fprintf(stderr
,
522 gettext(ERR_INSTALL_FAIL
),
526 remove_slashes(foundpath
);
528 if (strlcpy(drv_path
, foundpath
, drv_path_size
)
535 next
= strtok((char *)NULL
, MOD_SEP
);
537 (void) strcpy(data
, pathsave
);
538 next
= strtok(data
, MOD_SEP
);
539 curdir
= curdir
->next
;
548 (void) fprintf(stderr
, gettext(USAGE
));
552 update_driver_classes(
556 /* make call to update the classes file */
557 return (append_to_file(driver_name
, classes
, driver_classes
,
566 return (append_to_minor_perm(driver_name
, perm_list
, minor_perm
));
571 * Complete the minor perm update by communicating the minor perm
572 * data to the kernel. This information is used by devfs to ensure
573 * that devices always have the correct permissions when attached.
574 * The minor perm file must be updated and the driver configured
575 * in the system for this step to complete correctly.
578 devfs_update_minor_perm(
584 if (basedir
== NULL
|| (strcmp(basedir
, "/") == 0)) {
585 if (devfs_add_minor_perm(driver_name
,
586 log_minorperm_error
) != 0) {
587 (void) fprintf(stderr
,
588 gettext(ERR_UPDATE_PERM
), driver_name
);
599 return (append_to_file(driver_name
, privlist
, extra_privs
,
604 * Check to see if the driver we are adding is a more specific
605 * driver for a device already attached to a less specific driver.
606 * In other words, see if this driver comes earlier on the compatible
607 * list of a device already attached to another driver.
608 * If so, the new node will not be created (since the device is
609 * already attached) but when the system reboots, it will attach to
610 * the new driver but not have a node - we need to warn the user
611 * if this is the case.
614 drv_name_conflict(di_node_t root_node
)
617 * walk the device tree checking each node
619 if (di_walk_node(root_node
, DI_WALK_SIBFIRST
, NULL
, devfs_node
) == -1) {
620 free_conflict_list(conflict_lst
);
621 conflict_lst
= (struct dev_list
*)NULL
;
622 (void) fprintf(stderr
, gettext(ERR_DEVTREE
));
626 if (conflict_lst
== NULL
)
627 /* no conflicts found */
635 * called via di_walk_node().
636 * called for each node in the device tree. We skip nodes that:
637 * 1. are not hw nodes (since they cannot have generic names)
638 * 2. that do not have a compatible property
639 * 3. whose node name = binding name.
640 * 4. nexus nodes - the name of a generic nexus node would
641 * not be affected by a driver change.
642 * Otherwise, we parse the compatible property, if we find a
643 * match with the new driver before we find a match with the
644 * current driver, then we have a conflict and we save the
649 devfs_node(di_node_t node
, void *arg
)
651 char *binding_name
, *node_name
, *compat_names
, *devfsnm
;
652 struct dev_list
*new_entry
;
653 char strbuf
[MAXPATHLEN
];
657 * if there is no compatible property, we don't
658 * have to worry about any conflicts.
660 if ((n_names
= di_compatible_names(node
, &compat_names
)) <= 0)
661 return (DI_WALK_CONTINUE
);
664 * if the binding name and the node name match, then
665 * either no driver existed that could be bound to this node,
666 * or the driver name is the same as the node name.
668 binding_name
= di_binding_name(node
);
669 node_name
= di_node_name(node
);
670 if ((binding_name
== NULL
) || (strcmp(node_name
, binding_name
) == 0))
671 return (DI_WALK_CONTINUE
);
674 * we can skip nexus drivers since they do not
675 * have major/minor number info encoded in their
676 * /devices name and therefore won't change.
678 if (di_driver_ops(node
) & DI_BUS_OPS
)
679 return (DI_WALK_CONTINUE
);
682 * check for conflicts
683 * If we do find that the new driver is a more specific driver
684 * than the driver already attached to the device, we'll save
685 * away the node name for processing later.
687 if (drv_name_match(compat_names
, n_names
, binding_name
, new_drv
)) {
688 devfsnm
= di_devfs_path(node
);
689 (void) snprintf(strbuf
, sizeof (strbuf
),
690 "%s%s", DEVFS_ROOT
, devfsnm
);
691 di_devfs_path_free(devfsnm
);
692 new_entry
= (struct dev_list
*)calloc(1,
693 sizeof (struct dev_list
));
694 if (new_entry
== (struct dev_list
*)NULL
) {
695 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
698 /* save the /devices name */
699 if ((new_entry
->dev_name
= strdup(strbuf
)) == NULL
) {
700 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
704 /* save the driver name */
705 if ((new_entry
->driver_name
= strdup(di_driver_name(node
)))
707 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
708 free(new_entry
->dev_name
);
712 /* check to see if this is a clone device */
714 new_entry
->clone
= 1;
716 /* add it to the list */
717 new_entry
->next
= conflict_lst
;
718 conflict_lst
= new_entry
;
721 return (DI_WALK_CONTINUE
);
725 clone(di_node_t node
)
727 di_minor_t minor
= DI_MINOR_NIL
;
729 while ((minor
= di_minor_next(node
, minor
)) != DI_MINOR_NIL
) {
730 if (di_minor_type(minor
) == DDM_ALIAS
)
736 * check to see if the new_name shows up on the compat list before
737 * the cur_name (driver currently attached to the device).
740 drv_name_match(char *compat_names
, int n_names
, char *cur_name
, char *new_name
)
744 if (strcmp(cur_name
, new_name
) == 0)
747 /* parse the coompatible list */
748 for (i
= 0; i
< n_names
; i
++) {
749 if (strcmp(compat_names
, new_name
) == 0) {
753 if (strcmp(compat_names
, cur_name
) == 0) {
756 compat_names
+= strlen(compat_names
) + 1;
762 * A more specific driver is being added for a device already attached
763 * to a less specific driver. Print out a general warning and if
764 * the force flag was passed in, give the user a hint as to what
765 * nodes may be affected in /devices and /dev
768 print_drv_conflict_info(int force
)
770 struct dev_list
*ptr
;
772 if (conflict_lst
== NULL
)
775 (void) fprintf(stderr
,
776 "\nA reconfiguration boot must be performed to "
778 (void) fprintf(stderr
, "installation of this driver.\n");
782 (void) fprintf(stderr
,
783 "\nThe following entries in /devices will be "
786 (void) fprintf(stderr
,
787 "\nDriver installation failed because the following\n");
788 (void) fprintf(stderr
,
789 "entries in /devices would be affected:\n\n");
793 while (ptr
!= NULL
) {
794 (void) fprintf(stderr
, "\t%s", ptr
->dev_name
);
796 (void) fprintf(stderr
, " (clone device)\n");
798 (void) fprintf(stderr
, "[:*]\n");
799 (void) fprintf(stderr
, "\t(Device currently managed by driver "
800 "\"%s\")\n\n", ptr
->driver_name
);
803 check_dev_dir(force
);
807 * use nftw to walk through /dev looking for links that match
808 * an entry in the conflict list.
811 check_dev_dir(int force
)
813 int walk_flags
= FTW_PHYS
| FTW_MOUNT
;
817 (void) fprintf(stderr
, "\nThe following entries in /dev will "
820 (void) fprintf(stderr
, "\nThe following entries in /dev would "
824 (void) nftw("/dev", dev_node
, ft_depth
, walk_flags
);
826 (void) fprintf(stderr
, "\n");
830 * checks a /dev link to see if it matches any of the conlficting
831 * /devices nodes in conflict_lst.
835 dev_node(const char *node
, const struct stat
*node_stat
, int flags
,
836 struct FTW
*ftw_info
)
838 char linkbuf
[MAXPATHLEN
];
839 struct dev_list
*ptr
;
841 if (readlink(node
, linkbuf
, MAXPATHLEN
) == -1)
846 while (ptr
!= NULL
) {
847 if (strstr(linkbuf
, ptr
->dev_name
) != NULL
)
848 (void) fprintf(stderr
, "\t%s\n", node
);
856 free_conflict_list(struct dev_list
*list
)
858 struct dev_list
*save
;
860 /* free up any dev_list structs we allocated. */
861 while (list
!= NULL
) {
864 free(save
->dev_name
);
870 elf_type(char *file
, char **elfdesc
, int *elf_type_ptr
)
876 if ((fd
= open(file
, O_RDONLY
)) < 0) {
877 (void) fprintf(stderr
, gettext(ERR_CANNOT_OPEN
), file
,
881 if (elf_version(EV_CURRENT
) == EV_NONE
) {
882 (void) fprintf(stderr
, gettext(ERR_ELF_VERSION
),
887 elf
= elf_begin(fd
, ELF_C_READ
, NULL
);
888 if (elf_kind(elf
) != ELF_K_ELF
) {
889 (void) fprintf(stderr
, gettext(ERR_ELF_KIND
), file
);
894 ident
= elf_getident(elf
, 0);
895 if (ident
[EI_CLASS
] == ELFCLASS32
) {
897 *elf_type_ptr
= ELFCLASS32
;
898 } else if (ident
[EI_CLASS
] == ELFCLASS64
) {
900 *elf_type_ptr
= ELFCLASS64
;
903 *elf_type_ptr
= ELFCLASSNONE
;
911 correct_location(char *drv_path
, char **drvelf_desc
, int *drvelf_type_ptr
)
914 char copy_drv_path
[MAXPATHLEN
];
915 char *token
= copy_drv_path
;
917 (void) strcpy(copy_drv_path
, drv_path
);
919 if (elf_type(drv_path
, drvelf_desc
, drvelf_type_ptr
) == ERROR
) {
922 token
= strtok(copy_drv_path
, DIR_SEP
);
923 while (token
!= NULL
) {
924 if (strcmp("drv", token
) == 0) {
925 token
= strtok((char *)NULL
, DIR_SEP
);
926 if (strcmp(DRVDIR64
, token
) == 0) {
927 if (*drvelf_type_ptr
== ELFCLASS64
)
929 (void) fprintf(stderr
, gettext(ERR_LOCATION
),
930 *drvelf_desc
, drv_path
);
933 if (*drvelf_type_ptr
== ELFCLASS32
)
935 (void) fprintf(stderr
, gettext(ERR_LOCATION
),
936 *drvelf_desc
, drv_path
);
940 token
= strtok((char *)NULL
, DIR_SEP
);
947 * Creates a two-element linked list of isa-specific subdirectories to
948 * search for each driver, which is is used by the function
949 * module_not_found() to convert the isa-independent modpath into an
950 * isa-specific path . The list is ordered depending on the machine
951 * architecture and instruction set architecture, corresponding to the
952 * order in which module_not_found() will search for the driver. This
953 * routine relies on an architecture not having more than two
954 * sub-architectures (e.g., sparc/sparcv9 or i386/amd64).
957 isaspec_drvmod_discovery()
961 moddir
= (struct drvmod_dir
*)calloc(1, sizeof (struct drvmod_dir
));
962 if (moddir
== NULL
) {
963 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
967 if (sysinfo(SI_ARCHITECTURE
, arch
, sizeof (arch
)) == -1) {
968 (void) fprintf(stderr
, gettext(ERR_SYSINFO_ARCH
));
972 if (strcmp(arch
, "sparc") == 0 || strcmp(arch
, "i386") == 0) {
973 moddir
->next
= (struct drvmod_dir
*)
974 calloc(1, sizeof (struct drvmod_dir
));
975 if (moddir
->next
== NULL
) {
976 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
979 if (kelf_type
== ELFCLASS64
) {
980 (void) strcpy(moddir
->direc
, DRVDIR64
);
981 (void) strcpy(moddir
->next
->direc
, "");
983 (void) strcpy(moddir
->direc
, "");
984 (void) strcpy(moddir
->next
->direc
, DRVDIR64
);
986 moddir
->next
->next
= NULL
;
989 (void) fprintf(stderr
, gettext(ERR_ARCH_NOT_SUPPORTED
), arch
);
995 remove_slashes(char *path
)
1001 while ((slash
= strchr(slash
, '/')) != NULL
) {
1002 remain_str
= ++slash
;
1003 while (*remain_str
== '/')
1005 if (slash
!= remain_str
)
1006 (void) strcpy(slash
, remain_str
);
1009 pathlen
= strlen(path
);
1010 if ((pathlen
> 1) && path
[pathlen
- 1] == '/')
1011 path
[pathlen
- 1] = '\0';
1015 * This is for ITU floppies to add packages to the miniroot
1018 ignore_root_basedir(void)
1020 struct stat statbuf
;
1022 return (stat("/ADD_DRV_IGNORE_ROOT_BASEDIR", &statbuf
) == 0);