1 /*
2  * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3  *               Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  * This file is part of TUD:OS and distributed under the terms of the
6  * GNU Lesser General Public License 2.1.
7  * Please see the COPYING-LGPL-2.1 file for details.
8  */
9 
10 
11 #include <features.h>
12 
13 #ifndef  __USE_ATFILE
14 # define __USE_ATFILE 1
15 #endif
16 
17 #include <l4/re/log>
18 #include <l4/re/env>
19 
20 #include <stdarg.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <limits.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <sys/ioctl.h>
27 #include <sys/mman.h>
28 #include <sys/uio.h>
29 #include <sys/time.h>
30 #include <l4/l4re_vfs/backend>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include "redirect.h"
35 
36 using namespace L4Re::Vfs;
37 using cxx::Ref_ptr;
38 
read(int fd,void * buf,size_t count)39 ssize_t read(int fd, void *buf, size_t count)
40 {
41   struct iovec iov;
42   iov.iov_base = buf;
43   iov.iov_len = count;
44   return readv(fd, &iov, 1);
45 }
46 
write(int fd,const void * buf,size_t count)47 ssize_t write(int fd, const void *buf, size_t count)
48 {
49   struct iovec iov;
50   iov.iov_base = const_cast<void*>(buf);
51   iov.iov_len = count;
52   return writev(fd, &iov, 1);
53 }
54 
copy_stat64_to_stat(struct stat * buf,struct stat64 * sb64)55 static void copy_stat64_to_stat(struct stat *buf, struct stat64 *sb64)
56 {
57   memset(buf, 0, sizeof(*buf));
58 
59   buf->st_dev = sb64->st_dev;
60   buf->st_ino = sb64->st_ino;
61   buf->st_mode = sb64->st_mode;
62   buf->st_nlink = sb64->st_nlink;
63   buf->st_uid = sb64->st_uid;
64   buf->st_gid = sb64->st_gid;
65   buf->st_rdev = sb64->st_rdev;
66   buf->st_size = sb64->st_size;
67   buf->st_blksize = sb64->st_blksize;
68   buf->st_blocks = sb64->st_blocks;
69   buf->st_atime = sb64->st_atime;
70   buf->st_mtime = sb64->st_mtime;
71   buf->st_ctime = sb64->st_ctime;
72 }
73 
fstat(int fd,struct stat * buf)74 int fstat(int fd, struct stat *buf) noexcept(noexcept(fstat(fd, buf)))
75 {
76   struct stat64 sb64;
77   int r = fstat64(fd, &sb64);
78   if (r < 0)
79     return r;
80 
81   copy_stat64_to_stat(buf, &sb64);
82   return r;
83 }
84 
85 #define ERRNO_RET(r) do { \
86   if ((r) < 0) \
87     {          \
88       errno = -(r); \
89       return -1; \
90     } } while (0)
91 
92 
93 namespace {
94 
__internal_get_dir(int dirfd,char const ** path)95 static Ref_ptr<File> __internal_get_dir(int dirfd, char const **path) L4_NOTHROW
96 {
97   Ops *vfs_ops = L4Re::Vfs::vfs_ops;
98   if (**path == '/')
99     {
100       while (**path == '/')
101 	++*path;
102 
103       return vfs_ops->get_root();
104     }
105   else if (dirfd == AT_FDCWD)
106     return vfs_ops->get_cwd();
107   else
108     return vfs_ops->get_file(dirfd);
109 }
110 
111 static char const *
__internal_resolvedir(int dirfd,const char * path,int flags,mode_t mode,Ref_ptr<File> * f)112 __internal_resolvedir(int dirfd, const char *path, int flags, mode_t mode,
113                       Ref_ptr<File> *f) L4_NOTHROW
114 {
115   (void)flags;
116   (void)mode;
117   Ref_ptr<File> dir = __internal_get_dir(dirfd, &path);
118   if (!dir)
119     return 0;
120 
121   return dir->get_mount(path, f);
122 }
123 
124 static int
__internal_resolve(int dirfd,const char * path,int flags,mode_t mode,Ref_ptr<File> * f)125 __internal_resolve(int dirfd, const char *path, int flags, mode_t mode,
126                    Ref_ptr<File> *f) L4_NOTHROW
127 {
128   Ref_ptr<File> dir = __internal_get_dir(dirfd, &path);
129   if (!dir)
130     return -EBADF;
131 
132   return dir->openat(path, flags, mode, f);
133 }
134 
135 static int
__internal_open(const char * path,int flags,mode_t mode)136 __internal_open(const char *path, int flags, mode_t mode) L4_NOTHROW
137 {
138   Ref_ptr<File> f;
139   int res = __internal_resolve(AT_FDCWD, path, flags, mode, &f);
140 
141   ERRNO_RET(res);
142 
143   int fd = L4Re::Vfs::vfs_ops->alloc_fd(f);
144 
145   ERRNO_RET(fd);
146   return fd;
147 }
148 }
149 
open(const char * name,int flags,...)150 int open(const char *name, int flags, ...)
151 {
152   mode_t mode = 0;
153 
154   if (flags & O_CREAT)
155     {
156       va_list v;
157       va_start(v, flags);
158       mode = va_arg(v, mode_t);
159       va_end(v);
160     }
161 
162   return __internal_open(name, flags, mode);
163 
164 }
165 
open64(const char * name,int flags,...)166 int open64(const char *name, int flags, ...)
167 {
168   mode_t mode = 0;
169 
170   if (flags & O_CREAT)
171     {
172       va_list v;
173       va_start(v, flags);
174       mode = va_arg(v, mode_t);
175       va_end(v);
176     }
177 
178   return __internal_open(name, flags, mode);
179 }
180 
ioctl(int fd,unsigned long request,...)181 extern "C" int ioctl(int fd, unsigned long request, ...)
182 noexcept(noexcept(ioctl(fd, request)))
183 {
184   va_list v;
185   va_start(v, request);
186 
187   L4B_FD;
188 
189   int r = file->ioctl(request, v);
190   va_end(v);
191   POST();
192 }
193 
194 #if !(defined(__USE_LARGEFILE64) && !defined(__LP64__))
195 // Duplicate here as uclibc only defines fcntl64
196 // with __USE_LARGEFILE64 && !__LP64__
197 extern "C" int fcntl64 (int __fd, int __cmd, ...);
198 #endif
199 
fcntl64(int fd,int cmd,...)200 extern "C" int fcntl64(int fd, int cmd, ...)
201 {
202   Ops *o = L4Re::Vfs::vfs_ops;
203   Ref_ptr<File> f = o->get_file(fd);
204 
205   if (!f)
206     {
207       errno = EBADF;
208       return -1;
209     }
210 
211   switch (cmd)
212     {
213     case F_DUPFD:
214     case F_DUPFD_CLOEXEC:
215       // 'arg' has the lowest fd ... so dup isn't the correct thing
216       return dup(fd);
217 
218     case F_GETFD:
219       return 0;
220     case F_SETFD:
221       return 0;
222 
223     case F_GETFL:
224       return f->get_status_flags();
225     case F_SETFL:
226       return 0;
227 
228     case F_GETLK:
229     case F_SETLK:
230     case F_SETLKW:
231       errno = EINVAL;
232       return -1;
233 
234     case F_GETOWN:
235       return 0;
236     case F_SETOWN:
237       errno = EINVAL;
238       return -1;
239 
240     case F_GETSIG:
241       return 0;
242     case F_SETSIG:
243       errno = EINVAL;
244       return -1;
245 
246     default:
247       errno = EINVAL;
248       return -1;
249     }
250 }
251 
fcntl(int fd,int cmd,...)252 extern "C" int fcntl(int fd, int cmd, ...)
253 {
254   unsigned long arg;
255   va_list v;
256 
257   va_start(v, cmd);
258   arg = va_arg(v, unsigned long);
259   va_end(v);
260 
261   return fcntl64(fd, cmd, arg);
262 }
263 
264 
lseek(int fd,off_t offset,int whence)265 off_t lseek(int fd, off_t offset, int whence)
266 noexcept(noexcept(lseek(fd, offset, whence)))
267 {
268   return lseek64(fd, offset, whence);
269 }
270 
ftruncate(int fd,off_t length)271 int ftruncate(int fd, off_t length)
272 noexcept(noexcept(ftruncate(fd, length)))
273 {
274   return ftruncate64(fd, length);
275 }
276 
lockf(int fd,int cmd,off_t len)277 int lockf(int fd, int cmd, off_t len)
278 {
279   (void)fd;
280   (void)cmd;
281   (void)len;
282   errno = EINVAL;
283   return -1;
284 }
285 
286 
287 
dup2(int oldfd,int newfd)288 extern "C" int dup2(int oldfd, int newfd)
289 noexcept(noexcept(dup2(oldfd, newfd)))
290 {
291   if (newfd < 0)
292     {
293       errno = EBADF;
294       return -1;
295     }
296 
297   Ops *o = L4Re::Vfs::vfs_ops;
298   Ref_ptr<File> oldf = o->get_file(oldfd);
299   if (!oldf)
300     {
301       errno = EBADF;
302       return -1;
303     }
304 
305   cxx::Pair<Ref_ptr<File>, int> res = o->set_fd(newfd, oldf);
306   if (res.second)
307     {
308       errno = res.second;
309       return -1;
310     }
311 
312   if (!res.first || res.first == oldf)
313     return newfd;
314 
315   // do the stuff for close;
316   res.first->unlock_all_locks();
317 
318   return newfd;
319 }
320 
dup(int oldfd)321 extern "C" int dup(int oldfd)
322 noexcept(noexcept(dup(oldfd)))
323 {
324   Ops *o = L4Re::Vfs::vfs_ops;
325   Ref_ptr<File> f = o->get_file(oldfd);
326   if (!f)
327     {
328       errno = EBADF;
329       return -1;
330     }
331 
332   int r = o->alloc_fd(f);
333   ERRNO_RET(r);
334   return r;
335 }
336 
337 
stat(const char * path,struct stat * buf)338 int stat(const char *path, struct stat *buf)
339 noexcept(noexcept(stat(path, buf)))
340 {
341   struct stat64 sb64;
342   int r = stat64(path, &sb64);
343   if (r < 0)
344     return r;
345 
346   copy_stat64_to_stat(buf, &sb64);
347   return r;
348 }
349 
lstat(const char * path,struct stat * buf)350 int lstat(const char *path, struct stat *buf)
351 noexcept(noexcept(lstat(path, buf)))
352 {
353   struct stat64 sb64;
354   int r = lstat64(path, &sb64);
355 
356   if (r < 0)
357     return r;
358 
359   copy_stat64_to_stat(buf, &sb64);
360   return r;
361 }
362 
close(int fd)363 int close(int fd)
364 noexcept(noexcept(close(fd)))
365 {
366   Ops *o = L4Re::Vfs::vfs_ops;
367   Ref_ptr<File> f = o->free_fd(fd);
368   if (!f)
369     {
370       errno = EBADF;
371       return -1;
372     }
373 
374   f->unlock_all_locks();
375   return 0;
376 }
377 
access(const char * path,int mode)378 int access(const char *path, int mode)
379 noexcept(noexcept(access(path, mode)))
380 { return faccessat(AT_FDCWD, path, mode, 0); }
381 
382 extern "C" ssize_t __getdents64(int fd, char *buf, size_t nbytes);
__getdents64(int fd,char * buf,size_t nbytes)383 extern "C" ssize_t __getdents64(int fd, char *buf, size_t nbytes)
384 {
385   Ops *o = L4Re::Vfs::vfs_ops;
386   Ref_ptr<File> fdo = o->get_file(fd);
387   if (!fdo)
388     {
389       errno = EBADF;
390       return -1;
391     }
392 
393   ssize_t r = fdo->getdents(buf, nbytes);
394   if (r < 0)
395     {
396       errno = -r;
397       return -1;
398     }
399 
400   return r;
401 }
402 
403 #if __WORDSIZE == 64
404 extern "C" ssize_t __getdents(int, char *, size_t);
L4_STRONG_ALIAS(__getdents64,__getdents)405 L4_STRONG_ALIAS(__getdents64,__getdents)
406 #endif
407 
408 
409 #define L4B_REDIRECT(ret, func, ptlist, plist) \
410   extern "C" ret func ptlist noexcept(noexcept(func plist))     \
411   {                                                             \
412     cxx::Ref_ptr<L4Re::Vfs::File> file;                   \
413     int res = __internal_resolve(AT_FDCWD, _a1, 0, 0, &file);   \
414     ERRNO_RET(res);                                             \
415     ret r = file->L4B_REDIRECT_FUNC(func)(L4B_STRIP_FIRST(plist));  \
416     POST();                                                     \
417   }
418 
419 #define L4B_REDIRECT_FUNC(func) func
420 L4B_REDIRECT_3(ssize_t, readlink, const char *, char *, size_t)
421 L4B_REDIRECT_2(int,     utime,    const char *, const struct utimbuf *)
422 L4B_REDIRECT_2(int,     utimes,   const char *, const struct timeval *)
423 #undef L4B_REDIRECT_FUNC
424 
425 #define L4B_REDIRECT_FUNC(func) f##func
426 L4B_REDIRECT_2(int,       stat64,      const char *, struct stat64 *)
427 L4B_REDIRECT_2(int,       chmod,       const char *, mode_t)
428 #undef L4B_REDIRECT_FUNC
429 
430 #define L4B_REDIRECT_FUNC(func) fstat64
431 L4B_REDIRECT_2(int,       lstat64,     const char *, struct stat64 *)
432 #undef L4B_REDIRECT_FUNC
433 #undef L4B_REDIRECT
434 
435 #define L4B_REDIRECT(ret, func, ptlist, plist) \
436   extern "C" ret func ptlist noexcept(noexcept(func plist))     \
437   {                                                             \
438     cxx::Ref_ptr<L4Re::Vfs::File> dir;                          \
439     _a1 = __internal_resolvedir(AT_FDCWD, _a1, 0, 0, &dir);     \
440     ret r = dir->func plist;     \
441     POST();                                                     \
442   }
443 
444 L4B_REDIRECT_1(int, unlink,  const char *)
445 L4B_REDIRECT_2(int, mkdir,   const char *, mode_t)
446 L4B_REDIRECT_1(int, rmdir,   const char *)
447 #undef L4B_REDIRECT
448 
449 #define L4B_REDIRECT(ret, func, ptlist, plist) \
450   extern "C" ret func ptlist noexcept(noexcept(func plist))     \
451   {                                                             \
452     cxx::Ref_ptr<L4Re::Vfs::File> dir1;                         \
453     cxx::Ref_ptr<L4Re::Vfs::File> dir2;                         \
454     _a1 = __internal_resolvedir(AT_FDCWD, _a1, 0, 0, &dir1);    \
455     _a2 = __internal_resolvedir(AT_FDCWD, _a2, 0, 0, &dir2);    \
456     ret r = dir1->func plist;     \
457     POST();                                                     \
458   }
459 
460 L4B_REDIRECT_2(int, rename,  const char *, const char *)
461 L4B_REDIRECT_2(int, link,    const char *, const char *)
462 L4B_REDIRECT_2(int, symlink, const char *, const char *)
463 #undef L4B_REDIRECT
464 
465 #define L4B_REDIRECT(ret, func, ptlist, plist) \
466   extern "C" ret func ptlist noexcept(noexcept(func plist))     \
467   {                                                             \
468     cxx::Ref_ptr<L4Re::Vfs::File> file;                         \
469     int res = __internal_resolve(AT_FDCWD, _a1, 0, 0, &file);   \
470     ERRNO_RET(res);                                             \
471     ret r = file->ftruncate64(L4B_STRIP_FIRST(plist));  \
472     POST();                                                     \
473   }
474 
475 L4B_REDIRECT_2(int, truncate, const char *, off_t)
476 L4B_REDIRECT_2(int, truncate64, const char *, off64_t)
477 
478 #undef L4B_REDIRECT
479 
480 #define L4B_REDIRECT(ret, func, ptlist, plist) \
481   extern "C" ret func ptlist noexcept(noexcept(func plist))     \
482   {                                                             \
483     cxx::Ref_ptr<L4Re::Vfs::File> dir;                          \
484     _a2 = __internal_resolvedir(_a1, _a2, 0, 0, &dir);          \
485     if (!_a2) \
486       { \
487 	errno = EBADF; \
488 	return -1; \
489       } \
490     ret r = dir->func(L4B_STRIP_FIRST(plist));                  \
491     POST();                                                     \
492   }
493 
494 //L4B_REDIRECT_3(int, unlinkat,  int, const char *, int)
495 L4B_REDIRECT_4(int,       faccessat,   int, const char *, int, int)
496 L4B_REDIRECT_4(int, fchmodat, int, const char *, mode_t, int)
497 L4B_REDIRECT_4(int, utimensat, int, const char *,
498                                const struct timespec *, int)
499 
500 
501 // ------------------------------------------------------
502 
503 //#include <stdio.h>
504 #include <l4/util/util.h>
505 
506 int select(int nfds, fd_set *readfds, fd_set *writefds,
507            fd_set *exceptfds, struct timeval *timeout)
508 {
509   (void)nfds; (void)readfds; (void)writefds; (void)exceptfds;
510   //printf("Call: %s(%d, %p, %p, %p, %p[%ld])\n", __func__, nfds, readfds, writefds, exceptfds, timeout, timeout->tv_usec + timeout->tv_sec * 1000000);
511 
512 #if 0
513   int us = timeout->tv_usec + timeout->tv_sec * 1000000;
514   l4_timeout_t to = l4_timeout(L4_IPC_TIMEOUT_NEVER,
515                                l4util_micros2l4to(us));
516 #endif
517 
518   // only the timeout for now
519   if (timeout)
520     l4_usleep(timeout->tv_usec + timeout->tv_sec * 1000000);
521   else
522     l4_sleep_forever();
523 
524   return 0;
525 }
526 
527 #undef L4B_REDIRECT
528 
529 #define L4B_REDIRECT(ret, func, ptlist, plist) \
530   ret func ptlist noexcept(noexcept(func plist)) \
531   {               \
532     L4Re::Vfs::Ops *o = L4Re::Vfs::vfs_ops; \
533     cxx::Ref_ptr<L4Re::Vfs::File> f = o->get_file(_a1); \
534     if (!f) \
535       { \
536 	errno = EBADF; \
537 	return -1; \
538       } \
539     ret r = f->func(L4B_STRIP_FIRST(plist)); \
540     POST(); \
541   }
542 
543 __BEGIN_DECLS
544 ssize_t preadv(int, const struct iovec *, int, off_t);
545 ssize_t pwritev(int, const struct iovec *, int, off_t);
546 __END_DECLS
547 
548 L4B_REDIRECT_2(int,       fstat64,     int, struct stat64 *)
549 L4B_REDIRECT_3(ssize_t,   readv,       int, const struct iovec *, int)
550 L4B_REDIRECT_3(ssize_t,   writev,      int, const struct iovec *, int)
551 L4B_REDIRECT_4(ssize_t,   preadv,      int, const struct iovec *, int, off_t)
552 L4B_REDIRECT_4(ssize_t,   pwritev,     int, const struct iovec *, int, off_t)
553 L4B_REDIRECT_3(__off64_t, lseek64,     int, __off64_t, int)
554 L4B_REDIRECT_2(int,       ftruncate64, int, off64_t)
555 L4B_REDIRECT_1(int,       fsync,       int)
556 L4B_REDIRECT_1(int,       fdatasync,   int)
557 L4B_REDIRECT_2(int,       fchmod,      int, mode_t)
558 
559 static char const * const _default_current_working_dir = "/";
560 static char *_current_working_dir = const_cast<char *>(_default_current_working_dir);
561 
free_cwd()562 static void free_cwd()
563 {
564   if (_current_working_dir != _default_current_working_dir)
565     free(_current_working_dir);
566 }
567 
chdir(const char * path)568 extern "C" int chdir(const char *path) noexcept(noexcept(chdir(path)))
569 {
570   Ref_ptr<File> f;
571   int res = __internal_resolve(AT_FDCWD, path, 0, 0, &f);
572   ERRNO_RET(res);
573 
574   if (*path == '/')
575     {
576       char *new_cwd = strdup(path);
577       if (!new_cwd)
578         {
579           errno = ENOMEM;
580           return -1;
581         }
582       free_cwd();
583       _current_working_dir = new_cwd;
584     }
585   else
586     {
587       unsigned len_cwd = strlen(_current_working_dir);
588       unsigned len_path = strlen(path);
589       char *new_cwd = (char *)malloc(len_cwd + len_path + 2);
590       if (!new_cwd)
591         {
592           errno = ENOMEM;
593           return -1;
594         }
595       memcpy(new_cwd, _current_working_dir, len_cwd);
596       if (new_cwd[len_cwd - 1] != '/')
597         new_cwd[len_cwd++] = '/';
598       memcpy(new_cwd + len_cwd, path, len_path + 1);
599 
600       free_cwd();
601       _current_working_dir = new_cwd;
602     }
603 
604   // would need to check whether 'f' is a directory
605   L4Re::Vfs::vfs_ops->set_cwd(f);
606 
607   return 0;
608 }
609 
fchdir(int fd)610 extern "C" int fchdir(int fd) noexcept(noexcept(fchdir(fd)))
611 {
612   L4Re::Vfs::Ops *o = L4Re::Vfs::vfs_ops;
613   cxx::Ref_ptr<L4Re::Vfs::File> f = o->get_file(fd);
614   if (!f)
615     {
616       errno = EBADF;
617       return -1;
618     }
619 
620   // would need to check whether 'f' is a directory
621   o->set_cwd(f);
622   return 0;
623 }
624 
625 extern "C"
pread64(int fd,void * buf,size_t count,off64_t offset)626 ssize_t pread64(int fd, void *buf, size_t count, off64_t offset)
627 {
628   struct iovec iov;
629   iov.iov_base = buf;
630   iov.iov_len = count;
631   return preadv(fd, &iov, 1, offset);
632 }
633 
634 extern "C"
pread(int fd,void * buf,size_t count,off_t offset)635 ssize_t pread(int fd, void *buf, size_t count, off_t offset)
636 {
637   return pread64(fd, buf, count, offset);
638 }
639 
640 extern "C"
pwrite64(int fd,const void * buf,size_t count,off64_t offset)641 ssize_t pwrite64(int fd, const void *buf, size_t count, off64_t offset)
642 {
643   struct iovec iov;
644   iov.iov_base = const_cast<void*>(buf);
645   iov.iov_len = count;
646   return pwritev(fd, &iov, 1, offset);
647 }
648 
649 extern "C"
pwrite(int fd,const void * buf,size_t count,off_t offset)650 ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset)
651 {
652   return pwrite64(fd, buf, count, offset);
653 }
654 
getcwd(char * buf,size_t size)655 extern "C" char *getcwd(char *buf, size_t size)
656 noexcept(noexcept(getcwd(buf, size)))
657 {
658   unsigned len_cwd = strlen(_current_working_dir) + 1;
659 
660   /* Posix mandates returning EINVAL if a buffer is supplied without a size */
661   if (buf != 0 && size == 0)
662     {
663       errno = EINVAL;
664       return 0;
665     }
666 
667   if (buf == 0 && size == 0)
668     size = len_cwd;
669 
670   if (len_cwd > size)
671     {
672       errno = ERANGE;
673       return 0;
674     }
675 
676   if (buf == 0)
677     buf = (char *)malloc(size);
678 
679   if (buf == 0)
680     {
681       errno = ENOMEM;
682       return 0;
683     }
684 
685   memcpy(buf, _current_working_dir, len_cwd);
686   return buf;
687 }
688 
chroot(const char * p)689 extern "C" int chroot(const char *p)
690 noexcept(noexcept(chroot(p)))
691 {
692   errno = EIO;
693   return -1;
694 }
695 
mkfifo(const char * p,mode_t m)696 extern "C" int mkfifo(const char *p, mode_t m)
697 noexcept(noexcept(mkfifo(p, m)))
698 {
699   /* mkfifo on Linux returns EPERM on unsupported file systems */
700   errno = EPERM;
701   return -1;
702 }
703 
mknod(const char * p,mode_t m,dev_t d)704 extern "C" int mknod(const char *p, mode_t m, dev_t d)
705 noexcept(noexcept(mknod(p, m, d)))
706 {
707   errno = EINVAL;
708   return -1;
709 }
710 
chown(const char *,uid_t,gid_t)711 int chown(const char *, uid_t, gid_t)
712 {
713   errno = EINVAL;
714   return -1;
715 }
716 
fchown(int,uid_t,gid_t)717 int fchown(int, uid_t, gid_t)
718 {
719   errno = EINVAL;
720   return -1;
721 }
722 
723 
lchown(const char * p,uid_t u,gid_t g)724 extern "C" int lchown(const char *p, uid_t u, gid_t g)
725 noexcept(noexcept(lchown(p, u, g)))
726 {
727   errno = EINVAL;
728   return -1;
729 }
730