Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / fhandler / procsys.cc
blob4fa00481ad45f93f9a6350d3667499a9ef5e007e
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
7 details. */
9 #include "winsup.h"
10 #include <stdlib.h>
11 #include "cygerrno.h"
12 #include "security.h"
13 #include "path.h"
14 #include "fhandler.h"
15 #include "dtable.h"
16 #include "cygheap.h"
17 #include <winioctl.h>
18 #include "ntdll.h"
19 #include "tls_pbuf.h"
21 #include <dirent.h>
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]; \
29 { \
30 const char *from; \
31 PWCHAR to; \
32 for (to = namebuf, from = get_name () + procsys_len; *from; \
33 to++, from++) \
34 /* The NT device namespace is ASCII only. */ \
35 *to = (*from == '/') ? L'\\' : (WCHAR) *from; \
36 if (to == namebuf) \
37 *to++ = L'\\'; \
38 *to = L'\0'; \
39 RtlInitUnicodeString ((p), namebuf); \
42 virtual_ftype_t
43 fhandler_procsys::exists (struct stat *buf)
45 UNICODE_STRING path;
46 UNICODE_STRING dir;
47 OBJECT_ATTRIBUTES attr;
48 IO_STATUS_BLOCK io;
49 NTSTATUS status;
50 HANDLE h;
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)
58 return virt_rootdir;
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))
76 internal = true;
77 NtClose (h);
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,
83 &attr);
84 debug_printf ("NtOpenSymbolicLinkObject: %y", status);
85 if (NT_SUCCESS (status))
87 /* If requested, check permissions. */
88 if (buf)
89 get_object_attribute (h, &buf->st_uid, &buf->st_gid, buf->st_mode);
90 NtClose (h);
91 return virt_symlink;
93 else if (status == STATUS_ACCESS_DENIED)
94 return virt_symlink;
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. */
101 if (buf)
102 get_object_attribute (h, &buf->st_uid, &buf->st_gid, buf->st_mode);
103 NtClose (h);
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)
114 return virt_none;
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)
123 return virt_fsfile;
124 /* Check for pipe errors, which make a good hint... */
125 if (status >= STATUS_PIPE_NOT_AVAILABLE && status <= STATUS_PIPE_BUSY)
126 return virt_pipe;
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. */
144 dir = path;
145 InitializeObjectAttributes (&attr, &dir, OBJ_CASE_INSENSITIVE,
146 NULL, NULL);
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
175 badly written. */
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;
191 else
192 file_type = (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
193 ? virt_fsdir : virt_fsfile;
196 NtClose (h);
198 /* That's it. Return type we found above. */
199 return file_type;
202 virtual_ftype_t
203 fhandler_procsys::exists ()
205 return exists (NULL);
208 fhandler_procsys::fhandler_procsys ():
209 fhandler_virtual ()
213 #define UNREADABLE_SYMLINK_CONTENT "<access denied>"
215 bool
216 fhandler_procsys::fill_filebuf ()
218 char *fnamep;
219 UNICODE_STRING path, target;
220 OBJECT_ATTRIBUTES attr;
221 NTSTATUS status;
222 HANDLE h;
223 tmp_pathbuf tp;
224 size_t len;
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))
232 goto unreadable;
233 RtlInitEmptyUnicodeString (&target, tp.w_get (),
234 (NT_MAX_PATH - 1) * sizeof (WCHAR));
235 status = NtQuerySymbolicLinkObject (h, &target, NULL);
236 NtClose (h);
237 if (!NT_SUCCESS (status))
238 goto unreadable;
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, '\\')))
244 *fnamep = '/';
245 return true;
247 unreadable:
248 filebuf = (char *) crealloc_abort (filebuf,
249 sizeof (UNREADABLE_SYMLINK_CONTENT));
250 strcpy (filebuf, UNREADABLE_SYMLINK_CONTENT);
251 return false;
255 fhandler_procsys::fstat (struct stat *buf)
257 const char *path = get_name ();
258 debug_printf ("fstat (%s)", path);
260 fhandler_base::fstat (buf);
261 /* Best bet. */
262 buf->st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
263 buf->st_uid = 544;
264 buf->st_gid = 18;
265 buf->st_dev = buf->st_rdev = dev ();
266 buf->st_ino = get_ino ();
267 switch (exists (buf))
269 case virt_directory:
270 case virt_rootdir:
271 case virt_fsdir:
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;
279 break;
280 case virt_file:
281 case virt_fsfile:
282 buf->st_mode |= S_IFREG;
283 break;
284 case virt_symlink:
285 buf->st_mode |= S_IFLNK;
286 break;
287 case virt_pipe:
288 buf->st_mode |= S_IFIFO;
289 break;
290 case virt_socket:
291 buf->st_mode |= S_IFSOCK;
292 break;
293 case virt_chr:
294 buf->st_mode |= S_IFCHR;
295 break;
296 case virt_blk:
297 buf->st_mode |= S_IFBLK;
298 break;
299 default:
300 set_errno (ENOENT);
301 return -1;
303 return 0;
306 DIR *
307 fhandler_procsys::opendir (int fd)
309 UNICODE_STRING path;
310 OBJECT_ATTRIBUTES attr;
311 NTSTATUS status;
312 HANDLE dir_hdl;
313 DIR *dir;
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);
321 return NULL;
324 void *dbi_buf = NULL;
325 ULONG size = 65536;
326 ULONG context = 0;
327 int iter;
328 for (iter = 0; iter <= 3; ++iter) /* Allows for a 512K buffer */
330 void *new_buf = realloc (dbi_buf, size);
331 if (!new_buf)
332 goto err;
333 dbi_buf = new_buf;
334 status = NtQueryDirectoryObject (dir_hdl, dbi_buf, size, FALSE, TRUE,
335 &context, NULL);
336 if (!NT_SUCCESS (status))
338 __seterrno_from_nt_status (status);
339 goto err;
341 if (status != STATUS_MORE_ENTRIES)
342 break;
343 size <<= 1;
345 if (iter > 3)
347 __seterrno_from_nt_status (STATUS_INSUFFICIENT_RESOURCES);
348 goto err;
350 if (!(dir = fhandler_virtual::opendir (fd)))
351 goto err;
352 /* Note that dir->__handle points to the buffer, it does NOT contain an
353 actual handle! */
354 dir->__handle = dbi_buf;
355 /* dir->__d_internal contains the number of objects returned in the buffer. */
356 dir->__d_internal = context;
357 return dir;
359 err:
360 NtClose (dir_hdl);
361 free (dbi_buf);
362 return NULL;
366 fhandler_procsys::readdir (DIR *dir, dirent *de)
368 PDIRECTORY_BASIC_INFORMATION dbi;
369 int res = EBADF;
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)
377 res = ENMFILE;
378 else
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))
384 de->d_type = DT_DIR;
385 else if (RtlEqualUnicodeString (&dbi->ObjectTypeName, &ro_u_natsyml,
386 FALSE))
387 de->d_type = DT_LNK;
388 else if (!RtlEqualUnicodeString (&dbi->ObjectTypeName, &ro_u_natdev,
389 FALSE))
390 de->d_type = DT_CHR;
391 else /* Can't nail down "Device" objects without further testing. */
392 de->d_type = DT_UNKNOWN;
393 ++dir->__d_position;
394 res = 0;
397 syscall_printf ("%d = readdir(%p, %p)", res, dir, de);
398 return res;
401 long
402 fhandler_procsys::telldir (DIR *dir)
404 return dir->__d_position;
407 void
408 fhandler_procsys::seekdir (DIR *dir, long pos)
410 if (pos < 0)
411 dir->__d_position = 0;
412 else if (pos > (__int32_t) dir->__d_internal)
413 dir->__d_position = (__int32_t) dir->__d_internal;
414 else
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);
429 void
430 fhandler_procsys::read (void *ptr, size_t& len)
432 fhandler_base::raw_read (ptr, len);
435 ssize_t
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)
444 int res = 0;
446 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
447 set_errno (EINVAL);
448 else
450 switch (exists (NULL))
452 case virt_directory:
453 case virt_rootdir:
454 if ((flags & O_ACCMODE) != O_RDONLY)
455 set_errno (EISDIR);
456 else
458 nohandle (true);
459 res = 1;
461 break;
462 case virt_none:
463 set_errno (ENOENT);
464 break;
465 default:
466 res = fhandler_base::open (flags, mode);
467 break;
470 syscall_printf ("%d = fhandler_procsys::open(%p, 0%o)", res, flags, mode);
471 return res;
475 fhandler_procsys::close ()
477 if (!nohandle ())
478 NtClose (get_handle ());
479 return fhandler_virtual::close ();
481 #if 0
483 fhandler_procsys::ioctl (unsigned int cmd, void *)
486 #endif