1 /* fhandler_procsys.cc: fhandler for native NT namespace.
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
23 /* Path of the /proc/sys filesystem */
24 const char procsys
[] = "/proc/sys";
25 const size_t procsys_len
= sizeof (procsys
) - 1;
27 #define mk_unicode_path(p) \
28 WCHAR namebuf[strlen (get_name ()) + 1]; \
32 for (to = namebuf, from = get_name () + procsys_len; *from; \
34 /* The NT device namespace is ASCII only. */ \
35 *to = (*from == '/') ? L'\\' : (WCHAR) *from; \
39 RtlInitUnicodeString ((p), namebuf); \
43 fhandler_procsys::exists (struct stat
*buf
)
47 OBJECT_ATTRIBUTES attr
;
51 FILE_BASIC_INFORMATION fbi
;
52 bool internal
= false;
53 bool desperate_parent_check
= false;
54 /* Default device type is character device. */
55 virtual_ftype_t file_type
= virt_chr
;
57 if (strlen (get_name ()) == procsys_len
)
59 mk_unicode_path (&path
);
61 /* Try to open parent dir. If it works, the object is definitely
62 an object within the internal namespace. We don't need to test
63 it for being a file or dir on the filesystem anymore. If the
64 error is STATUS_OBJECT_TYPE_MISMATCH, we know that the file
65 itself is external. Otherwise we don't know. */
66 RtlSplitUnicodePath (&path
, &dir
, NULL
);
67 /* RtlSplitUnicodePath preserves the trailing backslash in dir. Don't
68 preserve it to open the dir, unless it's the object root. */
69 if (dir
.Length
> sizeof (WCHAR
))
70 dir
.Length
-= sizeof (WCHAR
);
71 InitializeObjectAttributes (&attr
, &dir
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
72 status
= NtOpenDirectoryObject (&h
, DIRECTORY_QUERY
, &attr
);
73 debug_printf ("NtOpenDirectoryObject: %y", status
);
74 if (NT_SUCCESS (status
))
80 /* First check if the object is a symlink. */
81 InitializeObjectAttributes (&attr
, &path
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
82 status
= NtOpenSymbolicLinkObject (&h
, READ_CONTROL
| SYMBOLIC_LINK_QUERY
,
84 debug_printf ("NtOpenSymbolicLinkObject: %y", status
);
85 if (NT_SUCCESS (status
))
87 /* If requested, check permissions. */
89 get_object_attribute (h
, &buf
->st_uid
, &buf
->st_gid
, buf
->st_mode
);
93 else if (status
== STATUS_ACCESS_DENIED
)
95 /* Then check if it's an object directory. */
96 status
= NtOpenDirectoryObject (&h
, READ_CONTROL
| DIRECTORY_QUERY
, &attr
);
97 debug_printf ("NtOpenDirectoryObject: %y", status
);
98 if (NT_SUCCESS (status
))
100 /* If requested, check permissions. */
102 get_object_attribute (h
, &buf
->st_uid
, &buf
->st_gid
, buf
->st_mode
);
104 return virt_directory
;
106 else if (status
== STATUS_ACCESS_DENIED
)
107 return virt_directory
;
108 /* Next try to open as file/device. */
109 status
= NtOpenFile (&h
, READ_CONTROL
| FILE_READ_ATTRIBUTES
, &attr
, &io
,
110 FILE_SHARE_VALID_FLAGS
, FILE_OPEN_FOR_BACKUP_INTENT
);
111 debug_printf ("NtOpenFile: %y", status
);
112 /* Name is invalid, that's nothing. */
113 if (status
== STATUS_OBJECT_NAME_INVALID
)
115 /* If no media is found, or we get this dreaded sharing violation, let
116 the caller immediately try again as normal file. */
117 if (status
== STATUS_NO_MEDIA_IN_DEVICE
118 || status
== STATUS_SHARING_VIOLATION
)
119 return virt_fsfile
; /* Just try again as normal file. */
120 /* If file or path can't be found, let caller try again as normal file. */
121 if (status
== STATUS_OBJECT_PATH_NOT_FOUND
122 || status
== STATUS_OBJECT_NAME_NOT_FOUND
)
124 /* Check for pipe errors, which make a good hint... */
125 if (status
>= STATUS_PIPE_NOT_AVAILABLE
&& status
<= STATUS_PIPE_BUSY
)
127 if (status
== STATUS_ACCESS_DENIED
&& !internal
)
129 /* Check if this is just some file or dir on a real FS to circumvent
130 most permission problems. Don't try that on internal objects,
131 since NtQueryAttributesFile might crash the machine if the underlying
132 driver is badly written. */
133 status
= NtQueryAttributesFile (&attr
, &fbi
);
134 debug_printf ("NtQueryAttributesFile: %y", status
);
135 if (NT_SUCCESS (status
))
136 return (fbi
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
137 ? virt_fsdir
: virt_fsfile
;
138 /* Ok, so we're desperate and the file still maybe on some filesystem.
139 To check this, we now split the path until we can finally access any
140 of the parent's. Then we fall through to check the parent type. In
141 contrast to the first parent check, we now check explicitely with
142 trailing backslash. This will fail for directories in the internal
143 namespace, so we won't accidentally test those. */
145 InitializeObjectAttributes (&attr
, &dir
, OBJ_CASE_INSENSITIVE
,
149 RtlSplitUnicodePath (&dir
, &dir
, NULL
);
150 status
= NtOpenFile (&h
, READ_CONTROL
| FILE_READ_ATTRIBUTES
,
151 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
152 FILE_OPEN_FOR_BACKUP_INTENT
);
153 debug_printf ("NtOpenDirectoryObject: %y", status
);
154 if (dir
.Length
> sizeof (WCHAR
))
155 dir
.Length
-= sizeof (WCHAR
);
157 while (dir
.Length
> sizeof (WCHAR
) && !NT_SUCCESS (status
));
158 desperate_parent_check
= true;
160 if (NT_SUCCESS (status
))
162 FILE_FS_DEVICE_INFORMATION ffdi
;
164 /* If requested, check permissions. If this is a parent handle from
165 the above desperate parent check, skip. */
166 if (buf
&& !desperate_parent_check
)
167 get_object_attribute (h
, &buf
->st_uid
, &buf
->st_gid
, buf
->st_mode
);
169 /* Check for the device type. */
170 status
= NtQueryVolumeInformationFile (h
, &io
, &ffdi
, sizeof ffdi
,
171 FileFsDeviceInformation
);
172 debug_printf ("NtQueryVolumeInformationFile: %y", status
);
173 /* Don't call NtQueryInformationFile unless we know it's a safe type.
174 The call is known to crash machines, if the underlying driver is
176 if (NT_SUCCESS (status
))
178 if (ffdi
.DeviceType
== FILE_DEVICE_NAMED_PIPE
)
179 file_type
= internal
? virt_blk
: virt_pipe
;
180 else if (ffdi
.DeviceType
== FILE_DEVICE_DISK
181 || ffdi
.DeviceType
== FILE_DEVICE_CD_ROM
182 || ffdi
.DeviceType
== FILE_DEVICE_VIRTUAL_DISK
)
184 /* Check for file attributes. If we get them, we peeked
185 into a real FS through /proc/sys. */
186 status
= NtQueryInformationFile (h
, &io
, &fbi
, sizeof fbi
,
187 FileBasicInformation
);
188 debug_printf ("NtQueryInformationFile: %y", status
);
189 if (!NT_SUCCESS (status
))
190 file_type
= virt_blk
;
192 file_type
= (fbi
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
193 ? virt_fsdir
: virt_fsfile
;
198 /* That's it. Return type we found above. */
203 fhandler_procsys::exists ()
205 return exists (NULL
);
208 fhandler_procsys::fhandler_procsys ():
213 #define UNREADABLE_SYMLINK_CONTENT "<access denied>"
216 fhandler_procsys::fill_filebuf ()
219 UNICODE_STRING path
, target
;
220 OBJECT_ATTRIBUTES attr
;
226 mk_unicode_path (&path
);
227 if (path
.Buffer
[path
.Length
/ sizeof (WCHAR
) - 1] == L
'\\')
228 path
.Length
-= sizeof (WCHAR
);
229 InitializeObjectAttributes (&attr
, &path
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
230 status
= NtOpenSymbolicLinkObject (&h
, SYMBOLIC_LINK_QUERY
, &attr
);
231 if (!NT_SUCCESS (status
))
233 RtlInitEmptyUnicodeString (&target
, tp
.w_get (),
234 (NT_MAX_PATH
- 1) * sizeof (WCHAR
));
235 status
= NtQuerySymbolicLinkObject (h
, &target
, NULL
);
237 if (!NT_SUCCESS (status
))
239 len
= sys_wcstombs (NULL
, 0, target
.Buffer
, target
.Length
/ sizeof (WCHAR
));
240 filebuf
= (char *) crealloc_abort (filebuf
, procsys_len
+ len
+ 1);
241 sys_wcstombs (fnamep
= stpcpy (filebuf
, procsys
), len
+ 1, target
.Buffer
,
242 target
.Length
/ sizeof (WCHAR
));
243 while ((fnamep
= strchr (fnamep
, '\\')))
248 filebuf
= (char *) crealloc_abort (filebuf
,
249 sizeof (UNREADABLE_SYMLINK_CONTENT
));
250 strcpy (filebuf
, UNREADABLE_SYMLINK_CONTENT
);
255 fhandler_procsys::fstat (struct stat
*buf
)
257 const char *path
= get_name ();
258 debug_printf ("fstat (%s)", path
);
260 fhandler_base::fstat (buf
);
262 buf
->st_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
;
265 buf
->st_dev
= buf
->st_rdev
= dev ();
266 buf
->st_ino
= get_ino ();
267 switch (exists (buf
))
272 buf
->st_mode
|= S_IFDIR
;
273 if (buf
->st_mode
& S_IRUSR
)
274 buf
->st_mode
|= S_IXUSR
;
275 if (buf
->st_mode
& S_IRGRP
)
276 buf
->st_mode
|= S_IXGRP
;
277 if (buf
->st_mode
& S_IROTH
)
278 buf
->st_mode
|= S_IXOTH
;
282 buf
->st_mode
|= S_IFREG
;
285 buf
->st_mode
|= S_IFLNK
;
288 buf
->st_mode
|= S_IFIFO
;
291 buf
->st_mode
|= S_IFSOCK
;
294 buf
->st_mode
|= S_IFCHR
;
297 buf
->st_mode
|= S_IFBLK
;
307 fhandler_procsys::opendir (int fd
)
310 OBJECT_ATTRIBUTES attr
;
315 mk_unicode_path (&path
);
316 InitializeObjectAttributes (&attr
, &path
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
317 status
= NtOpenDirectoryObject (&dir_hdl
, DIRECTORY_QUERY
, &attr
);
318 if (!NT_SUCCESS (status
))
320 __seterrno_from_nt_status (status
);
324 void *dbi_buf
= NULL
;
328 for (iter
= 0; iter
<= 3; ++iter
) /* Allows for a 512K buffer */
330 void *new_buf
= realloc (dbi_buf
, size
);
334 status
= NtQueryDirectoryObject (dir_hdl
, dbi_buf
, size
, FALSE
, TRUE
,
336 if (!NT_SUCCESS (status
))
338 __seterrno_from_nt_status (status
);
341 if (status
!= STATUS_MORE_ENTRIES
)
347 __seterrno_from_nt_status (STATUS_INSUFFICIENT_RESOURCES
);
350 if (!(dir
= fhandler_virtual::opendir (fd
)))
352 /* Note that dir->__handle points to the buffer, it does NOT contain an
354 dir
->__handle
= dbi_buf
;
355 /* dir->__d_internal contains the number of objects returned in the buffer. */
356 dir
->__d_internal
= context
;
366 fhandler_procsys::readdir (DIR *dir
, dirent
*de
)
368 PDIRECTORY_BASIC_INFORMATION dbi
;
371 if (dir
->__handle
!= INVALID_HANDLE_VALUE
)
373 dbi
= ((PDIRECTORY_BASIC_INFORMATION
) dir
->__handle
);
374 dbi
+= dir
->__d_position
;
375 if (dir
->__d_position
>= (__int32_t
) dir
->__d_internal
376 || dbi
->ObjectName
.Length
== 0)
380 sys_wcstombs (de
->d_name
, NAME_MAX
+ 1, dbi
->ObjectName
.Buffer
,
381 dbi
->ObjectName
.Length
/ sizeof (WCHAR
));
382 de
->d_ino
= hash_path_name (get_ino (), de
->d_name
);
383 if (RtlEqualUnicodeString (&dbi
->ObjectTypeName
, &ro_u_natdir
, FALSE
))
385 else if (RtlEqualUnicodeString (&dbi
->ObjectTypeName
, &ro_u_natsyml
,
388 else if (!RtlEqualUnicodeString (&dbi
->ObjectTypeName
, &ro_u_natdev
,
391 else /* Can't nail down "Device" objects without further testing. */
392 de
->d_type
= DT_UNKNOWN
;
397 syscall_printf ("%d = readdir(%p, %p)", res
, dir
, de
);
402 fhandler_procsys::telldir (DIR *dir
)
404 return dir
->__d_position
;
408 fhandler_procsys::seekdir (DIR *dir
, long pos
)
411 dir
->__d_position
= 0;
412 else if (pos
> (__int32_t
) dir
->__d_internal
)
413 dir
->__d_position
= (__int32_t
) dir
->__d_internal
;
415 dir
->__d_position
= pos
;
419 fhandler_procsys::closedir (DIR *dir
)
421 if (dir
->__handle
!= INVALID_HANDLE_VALUE
)
423 free (dir
->__handle
);
424 dir
->__handle
= INVALID_HANDLE_VALUE
;
426 return fhandler_virtual::closedir (dir
);
430 fhandler_procsys::read (void *ptr
, size_t& len
)
432 fhandler_base::raw_read (ptr
, len
);
436 fhandler_procsys::write (const void *ptr
, size_t len
)
438 return fhandler_base::raw_write (ptr
, len
);
442 fhandler_procsys::open (int flags
, mode_t mode
)
446 if ((flags
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
))
450 switch (exists (NULL
))
454 if ((flags
& O_ACCMODE
) != O_RDONLY
)
466 res
= fhandler_base::open (flags
, mode
);
470 syscall_printf ("%d = fhandler_procsys::open(%p, 0%o)", res
, flags
, mode
);
475 fhandler_procsys::close ()
478 NtClose (get_handle ());
479 return fhandler_virtual::close ();
483 fhandler_procsys::ioctl (unsigned int cmd
, void *)