From 9ca7bca3c381d4affa78b450bd7284da9a68ec9b Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Thu, 29 Mar 2012 18:02:54 +0000 Subject: [PATCH] * Makefile.in (DLL_OFILES): Add fhandler_dev.o. * devices.h (DEV_DEV_MAJOR): Define. (FH_DEV): Redefine in terms of DEV_DEV_MAJOR. (ext_dev_storage): Declare. (dev_storage_size): Declare. (dev_dev_storage): Declare. (dev_dev): Define. (isdev_dev): Define. * devices.in (dev_dev_storage): Activate. (ext_dev_storage): Define as externally available pointer to dev_storage. (dev_storage_size): Define to contain number of dev_storage elements. * dir.cc (rmdir): Handle /dev as always not empty. * dtable.cc (fh_alloc): Handle DEV_DEV_MAJOR. * fhandler.h (fhandler_dev): New class, derived from fhandler_disk_file. (fhandler_union): Add fhandler_dev member. * fhandler_disk_file.cc (class __DIR_mounts): Handle /dev directory to make sure it always exists. * fhandler_dev.cc: New file implementing /dev. * globals.cc (ro_u_dev): New R/O unicode string. * path.cc (path_conv::check): Handle FH_DEV device. --- winsup/cygwin/ChangeLog | 24 +++++ winsup/cygwin/Makefile.in | 38 ++++---- winsup/cygwin/devices.cc | 6 +- winsup/cygwin/devices.h | 13 ++- winsup/cygwin/devices.in | 6 +- winsup/cygwin/dir.cc | 5 + winsup/cygwin/dtable.cc | 3 + winsup/cygwin/fhandler.h | 29 ++++++ winsup/cygwin/fhandler_dev.cc | 145 ++++++++++++++++++++++++++++ winsup/cygwin/fhandler_disk_file.cc | 76 ++++++++++++++- winsup/cygwin/globals.cc | 1 + winsup/cygwin/path.cc | 16 +-- 12 files changed, 325 insertions(+), 37 deletions(-) create mode 100644 winsup/cygwin/fhandler_dev.cc diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index bff0e57f6..cbefec3f1 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,27 @@ +2012-03-29 Corinna Vinschen + + * Makefile.in (DLL_OFILES): Add fhandler_dev.o. + * devices.h (DEV_DEV_MAJOR): Define. + (FH_DEV): Redefine in terms of DEV_DEV_MAJOR. + (ext_dev_storage): Declare. + (dev_storage_size): Declare. + (dev_dev_storage): Declare. + (dev_dev): Define. + (isdev_dev): Define. + * devices.in (dev_dev_storage): Activate. + (ext_dev_storage): Define as externally available pointer to + dev_storage. + (dev_storage_size): Define to contain number of dev_storage elements. + * dir.cc (rmdir): Handle /dev as always not empty. + * dtable.cc (fh_alloc): Handle DEV_DEV_MAJOR. + * fhandler.h (fhandler_dev): New class, derived from fhandler_disk_file. + (fhandler_union): Add fhandler_dev member. + * fhandler_disk_file.cc (class __DIR_mounts): Handle /dev directory + to make sure it always exists. + * fhandler_dev.cc: New file implementing /dev. + * globals.cc (ro_u_dev): New R/O unicode string. + * path.cc (path_conv::check): Handle FH_DEV device. + 2012-03-29 Christopher Faylor * fhandler_console.cc (fhandler_console::set_unit): Don't succeed diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index 4830e40d5..e65b44031 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -139,25 +139,25 @@ DLL_OFILES:=advapi32.o assert.o autoload.o bsdlib.o ctype.o cxx.o cygheap.o \ cygthread.o cygtls.o cygxdr.o dcrt0.o debug.o devices.o dir.o dlfcn.o \ dll_init.o dtable.o environ.o errno.o exceptions.o exec.o external.o \ fcntl.o fenv.o fhandler.o fhandler_clipboard.o fhandler_console.o \ - fhandler_disk_file.o fhandler_dsp.o fhandler_fifo.o fhandler_floppy.o \ - fhandler_mailslot.o fhandler_mem.o fhandler_netdrive.o fhandler_nodevice.o \ - fhandler_proc.o fhandler_process.o fhandler_procnet.o fhandler_procsys.o \ - fhandler_procsysvipc.o fhandler_random.o fhandler_raw.o fhandler_registry.o \ - fhandler_serial.o fhandler_socket.o fhandler_tape.o fhandler_termios.o \ - fhandler_tty.o fhandler_virtual.o fhandler_windows.o fhandler_zero.o \ - flock.o fnmatch.o fork.o fts.o ftw.o getopt.o glob.o glob_pattern_p.o \ - globals.o grp.o heap.o hookapi.o inet_addr.o inet_network.o init.o ioctl.o \ - ipc.o kernel32.o libstdcxx_wrapper.o localtime.o lsearch.o malloc_wrapper.o \ - minires-os-if.o minires.o miscfuncs.o mktemp.o mmap.o msg.o \ - mount.o net.o netdb.o nfs.o nftw.o nlsfuncs.o ntea.o passwd.o path.o \ - pinfo.o pipe.o poll.o posix_ipc.o pseudo-reloc.o pthread.o random.o \ - regcomp.o regerror.o regexec.o regfree.o registry.o resource.o rexec.o \ - rcmd.o scandir.o sched.o sec_acl.o sec_auth.o sec_helper.o security.o \ - select.o sem.o setlsapwd.o shared.o shm.o sigfe.o signal.o sigproc.o \ - smallprint.o spawn.o strace.o strfmon.o strfuncs.o strptime.o strsep.o \ - strsig.o sync.o syscalls.o sysconf.o syslog.o termios.o thread.o \ - timer.o times.o tls_pbuf.o tty.o uinfo.o uname.o wait.o wincap.o \ - window.o winf.o wow64.o xsique.o \ + fhandler_dev.o fhandler_disk_file.o fhandler_dsp.o fhandler_fifo.o \ + fhandler_floppy.o fhandler_mailslot.o fhandler_mem.o fhandler_netdrive.o \ + fhandler_nodevice.o fhandler_proc.o fhandler_process.o fhandler_procnet.o \ + fhandler_procsys.o fhandler_procsysvipc.o fhandler_random.o fhandler_raw.o \ + fhandler_registry.o fhandler_serial.o fhandler_socket.o fhandler_tape.o \ + fhandler_termios.o fhandler_tty.o fhandler_virtual.o fhandler_windows.o \ + fhandler_zero.o flock.o fnmatch.o fork.o fts.o ftw.o getopt.o glob.o \ + glob_pattern_p.o globals.o grp.o heap.o hookapi.o inet_addr.o \ + inet_network.o init.o ioctl.o ipc.o kernel32.o libstdcxx_wrapper.o \ + localtime.o lsearch.o malloc_wrapper.o minires-os-if.o minires.o \ + miscfuncs.o mktemp.o mmap.o msg.o mount.o net.o netdb.o nfs.o nftw.o \ + nlsfuncs.o ntea.o passwd.o path.o pinfo.o pipe.o poll.o posix_ipc.o \ + pseudo-reloc.o pthread.o random.o regcomp.o regerror.o regexec.o regfree.o \ + registry.o resource.o rexec.o rcmd.o scandir.o sched.o sec_acl.o \ + sec_auth.o sec_helper.o security.o select.o sem.o setlsapwd.o shared.o \ + shm.o sigfe.o signal.o sigproc.o smallprint.o spawn.o strace.o strfmon.o \ + strfuncs.o strptime.o strsep.o strsig.o sync.o syscalls.o sysconf.o \ + syslog.o termios.o thread.o timer.o times.o tls_pbuf.o tty.o uinfo.o \ + uname.o wait.o wincap.o window.o winf.o wow64.o xsique.o \ $(EXTRA_DLL_OFILES) $(EXTRA_OFILES) $(MALLOC_OFILES) $(MT_SAFE_OBJECTS) EXCLUDE_STATIC_OFILES:=$(addprefix --exclude=,\ diff --git a/winsup/cygwin/devices.cc b/winsup/cygwin/devices.cc index 432f3359d..bfd4bcf89 100644 --- a/winsup/cygwin/devices.cc +++ b/winsup/cygwin/devices.cc @@ -33,10 +33,8 @@ const device dev_procsysvipc_storage = const device dev_netdrive_storage = {"", {FH_NETDRIVE}, ""}; -#if 0 const device dev_dev_storage = {"/dev", {FH_DEV}, "/dev"}; -#endif const device dev_registry_storage = {"", {FH_REGISTRY}, ""}; @@ -46531,6 +46529,10 @@ return NULL; #undef BRACK + +const device *ext_dev_storage = dev_storage; +const size_t dev_storage_size = sizeof dev_storage / sizeof dev_storage[0]; + void device::parse (const char *s) { diff --git a/winsup/cygwin/devices.h b/winsup/cygwin/devices.h index 96eaa3ec4..ff93da919 100644 --- a/winsup/cygwin/devices.h +++ b/winsup/cygwin/devices.h @@ -1,6 +1,6 @@ /* devices.h - Copyright 2002, 2003, 2004, 2005, 2007, 2009, 2010, 2011 Red Hat, Inc. + Copyright 2002, 2003, 2004, 2005, 2007, 2009, 2010, 2011, 2012 Red Hat, Inc. This file is part of Cygwin. @@ -68,7 +68,6 @@ enum fh_devices FH_FIFO = FHDEV (0, 196), FH_FS = FHDEV (0, 195), /* filesystem based device */ FH_NETDRIVE= FHDEV (0, 194), - FH_DEV = FHDEV (0, 193), DEV_FLOPPY_MAJOR = 2, FH_FLOPPY = FHDEV (DEV_FLOPPY_MAJOR, 0), @@ -243,6 +242,9 @@ enum fh_devices DEV_CYGDRIVE_MAJOR = 98, FH_CYGDRIVE= FHDEV (DEV_CYGDRIVE_MAJOR, 0), + DEV_DEV_MAJOR = 99, + FH_DEV= FHDEV (DEV_DEV_MAJOR, 0), + DEV_TCP_MAJOR = 30, FH_TCP = FHDEV (DEV_TCP_MAJOR, 36), FH_UDP = FHDEV (DEV_TCP_MAJOR, 39), @@ -316,6 +318,9 @@ struct device inline bool is_fs_special () const {return dev_on_fs && d.devn != FH_FS;} }; +extern const device *ext_dev_storage; +extern const size_t dev_storage_size; + extern const device *console_dev; extern const device *ptmx_dev; extern const device *ptys_dev; @@ -336,6 +341,8 @@ extern const device dev_pipew_storage; #define pipew_dev (&dev_pipew_storage) extern const device dev_proc_storage; #define proc_dev (&dev_proc_storage) +extern const device dev_dev_storage; +#define dev_dev (&dev_dev_storage) extern const device dev_netdrive_storage; #define netdrive_dev (&dev_netdrive_storage) extern const device dev_cygdrive_storage; @@ -348,6 +355,8 @@ extern const device dev_fs_storage; #define isproc_dev(devn) \ (devn >= FH_PROC_MIN_MINOR && devn <= FH_PROC_MAX_MINOR) +#define isdev_dev(devn) (devn == FH_DEV) + #define isprocsys_dev(devn) (devn == FH_PROCSYS) #define isvirtual_dev(devn) \ diff --git a/winsup/cygwin/devices.in b/winsup/cygwin/devices.in index b404e425d..553478413 100644 --- a/winsup/cygwin/devices.in +++ b/winsup/cygwin/devices.in @@ -29,10 +29,8 @@ const device dev_procsysvipc_storage = const device dev_netdrive_storage = {"", {FH_NETDRIVE}, ""}; -#if 0 const device dev_dev_storage = {"/dev", {FH_DEV}, "/dev"}; -#endif const device dev_registry_storage = {"", {FH_REGISTRY}, ""}; @@ -108,6 +106,10 @@ const device dev_error_storage = %other {return NULL;} %% #undef BRACK + +const device *ext_dev_storage = dev_storage; +const size_t dev_storage_size = sizeof dev_storage / sizeof dev_storage[0]; + void device::parse (const char *s) { diff --git a/winsup/cygwin/dir.cc b/winsup/cygwin/dir.cc index 2fb6eac6e..5ecd0a6d7 100644 --- a/winsup/cygwin/dir.cc +++ b/winsup/cygwin/dir.cc @@ -347,6 +347,11 @@ rmdir (const char *dir) set_errno (ENOENT); else if (has_dot_last_component (dir, false)) set_errno (EINVAL); + else if (isdev_dev (fh->dev ())) + { + set_errno (ENOTEMPTY); + goto done; + } else if (!fh->rmdir ()) res = 0; diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index 69add318c..1d59eccfb 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -459,6 +459,9 @@ fh_alloc (path_conv& pc) case DEV_CYGDRIVE_MAJOR: fh = cnew (fhandler_cygdrive); break; + case DEV_DEV_MAJOR: + fh = cnew (fhandler_dev); + break; case DEV_FLOPPY_MAJOR: case DEV_CDROM_MAJOR: case DEV_SD_MAJOR: diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 533a9d4a4..4cdf81e86 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1016,6 +1016,34 @@ class fhandler_disk_file: public fhandler_base } }; +class fhandler_dev: public fhandler_disk_file +{ + int lastrealpos; + bool dir_exists; +public: + fhandler_dev (); + DIR *opendir (int fd) __attribute__ ((regparm (2))); + int readdir (DIR *, dirent *) __attribute__ ((regparm (3))); + void rewinddir (DIR *); + + fhandler_dev (void *) {} + + void copyto (fhandler_base *x) + { + x->pc.free_strings (); + *reinterpret_cast (x) = *this; + x->reset (this); + } + + fhandler_dev *clone (cygheap_types malloc_type = HEAP_FHANDLER) + { + void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_dev)); + fhandler_dev *fh = new (ptr) fhandler_dev (ptr); + copyto (fh); + return fh; + } +}; + class fhandler_cygdrive: public fhandler_disk_file { enum @@ -2093,6 +2121,7 @@ typedef union { char __base[sizeof (fhandler_base)]; char __console[sizeof (fhandler_console)]; + char __dev[sizeof (fhandler_dev)]; char __cygdrive[sizeof (fhandler_cygdrive)]; char __dev_clipboard[sizeof (fhandler_dev_clipboard)]; char __dev_dsp[sizeof (fhandler_dev_dsp)]; diff --git a/winsup/cygwin/fhandler_dev.cc b/winsup/cygwin/fhandler_dev.cc new file mode 100644 index 000000000..ad39961e5 --- /dev/null +++ b/winsup/cygwin/fhandler_dev.cc @@ -0,0 +1,145 @@ +/* fhandler_dev.cc, Implement /dev. + + Copyright 2012 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include +#include "path.h" +#include "fhandler.h" +#include "shared_info.h" +#include "ntdll.h" +#include "devices.h" + +#define _COMPILING_NEWLIB +#include + +#define dev_prefix_len (sizeof ("/dev")) + +static int +device_cmp (const void *a, const void *b) +{ + return strcmp (((const device *) a)->name, + ((const device *) b)->name + dev_prefix_len); +} + +int +fhandler_dev::readdir (DIR *dir, dirent *de) +{ + int ret; + + if (dir_exists && !lastrealpos) + { + while ((ret = fhandler_disk_file::readdir (dir, de)) == 0) + { + /* Avoid to print devices for which users have created files under + /dev already, for instance by using the old script from Igor + Peshansky. */ + device dev; + dev.name = de->d_name; + if (!bsearch (&dev, ext_dev_storage, dev_storage_size, sizeof dev, + device_cmp)) + break; + } + if (ret == ENMFILE) + lastrealpos = dir->__d_position; + } + if (!dir_exists || lastrealpos) + { + ret = ENMFILE; + for (size_t idx = dir->__d_position - lastrealpos + 1; + idx < dev_storage_size; + ++idx) + { + struct __stat64 st; + + ++dir->__d_position; + /* Exclude devices which are only available for internal purposes + and devices which are not really existing at this time. */ + switch (ext_dev_storage[idx].d.major) + { + case 0: + if (ext_dev_storage[idx].d.minor == FH_FIFO + || ext_dev_storage[idx].d.minor == FH_PIPE) + continue; + case DEV_PTYM_MAJOR: + if (ext_dev_storage[idx].d.minor + || !strcmp (ext_dev_storage[idx].name, "/dev/ptm0")) + continue; + break; + case DEV_PTYS_MAJOR: + if (cygwin_shared->tty.connect (ext_dev_storage[idx].d.minor) + == -1) + continue; + break; + case DEV_CONS_MAJOR: + if (!iscons_dev (myself->ctty) + || myself->ctty != ext_dev_storage[idx].d.devn_int) + continue; + break; + case DEV_FLOPPY_MAJOR: + case DEV_TAPE_MAJOR: + case DEV_CDROM_MAJOR: + case DEV_SERIAL_MAJOR: + case DEV_SD_MAJOR: + case DEV_SD1_MAJOR: + case DEV_SD2_MAJOR: + case DEV_SD3_MAJOR: + case DEV_SD4_MAJOR: + case DEV_SD5_MAJOR: + case DEV_SD6_MAJOR: + case DEV_SD7_MAJOR: + { + WCHAR wpath[MAX_PATH]; + UNICODE_STRING upath; + OBJECT_ATTRIBUTES attr; + HANDLE h; + NTSTATUS status; + + sys_mbstowcs (wpath, MAX_PATH, ext_dev_storage[idx].native); + RtlInitUnicodeString (&upath, wpath); + InitializeObjectAttributes (&attr, &upath, + OBJ_CASE_INSENSITIVE, NULL, NULL); + /* The native paths are devices, not symlinks, so we expect + a matching error message. */ + status = NtOpenSymbolicLinkObject (&h, SYMBOLIC_LINK_QUERY, + &attr); + switch (status) + { + case STATUS_OBJECT_NAME_NOT_FOUND: + case STATUS_OBJECT_PATH_NOT_FOUND: + continue; + case STATUS_SUCCESS: + NtClose (h); + break; + default: + break; + } + } + break; + } + if (!lstat64 (ext_dev_storage[idx].name, &st)) + { + strcpy (de->d_name, ext_dev_storage[idx].name + dev_prefix_len); + de->d_ino = st.st_ino; + de->d_type = S_ISBLK (st.st_mode) ? DT_BLK : DT_CHR; + ret = 0; + break; + } + } + } + return ret; +} + +void +fhandler_dev::rewinddir (DIR *dir) +{ + lastrealpos = 0; + fhandler_disk_file::rewinddir (dir); +} + diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index 1fe12b0a2..3f54ebefa 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -26,6 +26,7 @@ details. */ #include "pwdgrp.h" #include #include +#include "devices.h" #define _COMPILING_NEWLIB #include @@ -36,11 +37,12 @@ class __DIR_mounts const char *parent_dir; int parent_dir_len; UNICODE_STRING mounts[MAX_MOUNTS]; - bool found[MAX_MOUNTS + 2]; + bool found[MAX_MOUNTS + 3]; UNICODE_STRING cygdrive; #define __DIR_PROC (MAX_MOUNTS) #define __DIR_CYGDRIVE (MAX_MOUNTS+1) +#define __DIR_DEV (MAX_MOUNTS+2) __ino64_t eval_ino (int idx) { @@ -84,6 +86,11 @@ public: found[__DIR_PROC] = true; return 2; } + if (RtlEqualUnicodeString (fname, &ro_u_dev, FALSE)) + { + found[__DIR_DEV] = true; + return 2; + } if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2 && RtlEqualUnicodeString (fname, &cygdrive, FALSE)) { @@ -121,6 +128,13 @@ public: *retname = ro_u_proc; return 2; } + if (!found[__DIR_DEV]) + { + found[__DIR_DEV] = true; + if (retname) + *retname = ro_u_dev; + return 2; + } if (!found[__DIR_CYGDRIVE]) { found[__DIR_CYGDRIVE] = true; @@ -2351,6 +2365,66 @@ fhandler_disk_file::closedir (DIR *dir) return res; } +fhandler_dev::fhandler_dev () : + fhandler_disk_file (), lastrealpos (0), dir_exists (true) +{ +} + +DIR * +fhandler_dev::opendir (int fd) +{ + DIR *dir; + DIR *res = NULL; + + dir = fhandler_disk_file::opendir (fd); + if (dir) + return dir; + if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL) + set_errno (ENOMEM); + else if ((dir->__d_dirent = + (struct dirent *) malloc (sizeof (struct dirent))) == NULL) + { + set_errno (ENOMEM); + goto free_dir; + } + else + { + cygheap_fdnew cfd; + if (cfd < 0 && fd < 0) + goto free_dirent; + + dir->__d_dirname = NULL; + dir->__d_dirent->__d_version = __DIRENT_VERSION; + dir->__d_cookie = __DIRENT_COOKIE; + dir->__handle = INVALID_HANDLE_VALUE; + dir->__d_position = 0; + dir->__flags = 0; + dir->__d_internal = 0; + + if (fd >= 0) + dir->__d_fd = fd; + else + { + cfd = this; + dir->__d_fd = cfd; + cfd->nohandle (true); + } + set_close_on_exec (true); + dir->__fh = this; + dir_exists = false; + res = dir; + } + + syscall_printf ("%p = opendir (%s)", res, get_name ()); + return res; + +free_dirent: + free (dir->__d_dirent); +free_dir: + free (dir); + return res; +} + fhandler_cygdrive::fhandler_cygdrive () : fhandler_disk_file (), ndrives (0), pdrive (NULL) { diff --git a/winsup/cygwin/globals.cc b/winsup/cygwin/globals.cc index 5a0e096d5..87b6cd84e 100644 --- a/winsup/cygwin/globals.cc +++ b/winsup/cygwin/globals.cc @@ -96,6 +96,7 @@ UNICODE_STRING _RDATA ro_u_com = _ROU (L".com"); UNICODE_STRING _RDATA ro_u_scr = _ROU (L".scr"); UNICODE_STRING _RDATA ro_u_sys = _ROU (L".sys"); UNICODE_STRING _RDATA ro_u_proc = _ROU (L"proc"); +UNICODE_STRING _RDATA ro_u_dev = _ROU (L"dev"); UNICODE_STRING _RDATA ro_u_pmem = _ROU (L"\\Device\\PhysicalMemory"); UNICODE_STRING _RDATA ro_u_natp = _ROU (L"\\??\\"); UNICODE_STRING _RDATA ro_u_uncp = _ROU (L"\\??\\UNC\\"); diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index ea984c2e5..b7f84caaa 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -730,17 +730,9 @@ path_conv::check (const char *src, unsigned opt, } goto out; } - else if (dev == FH_DEV) + else if (isdev_dev (dev)) { - dev = FH_FS; -#if 0 - fileattr = getfileattr (THIS_path, sym.pflags & MOUNT_NOPOSIX); - if (!component && fileattr == INVALID_FILE_ATTRIBUTES) - { - fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY; - goto out; - } -#endif + /* Just make sure that the path handling goes on as with FH_FS. */ } else if (isvirtual_dev (dev)) { @@ -879,7 +871,9 @@ is_virtual_symlink: if (!component) { - fileattr = sym.fileattr; + /* Make sure that /dev always exists. */ + fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY + : sym.fileattr; path_flags = sym.pflags; }