/* fhandler_virtual.cc: base fhandler class for virtual filesystems Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 #include "cygerrno.h" #include "path.h" #include "fhandler.h" #include "dtable.h" #include "cygheap.h" #include "sync.h" #include "child_info.h" #include fhandler_virtual::fhandler_virtual (): fhandler_base (), filebuf (NULL), fileid (-1) { } fhandler_virtual::~fhandler_virtual () { if (filebuf) { cfree (filebuf); filebuf = NULL; } } void fhandler_virtual::fixup_after_exec () { } DIR * fhandler_virtual::opendir (int fd) { DIR *dir; DIR *res = NULL; size_t len; if (exists () <= 0) set_errno (ENOTDIR); else if ((len = strlen (get_name ())) > PATH_MAX - 3) set_errno (ENAMETOOLONG); else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL) set_errno (ENOMEM); else if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL) { free (dir); set_errno (ENOMEM); } else if ((dir->__d_dirent = (struct dirent *) malloc (sizeof (struct dirent))) == NULL) { free (dir->__d_dirname); free (dir); set_errno (ENOMEM); } else { strcpy (dir->__d_dirname, get_name ()); dir->__d_dirent->__d_version = __DIRENT_VERSION; dir->__d_cookie = __DIRENT_COOKIE; dir->__handle = INVALID_HANDLE_VALUE; dir->__d_position = 0; dir->__flags = 0; if (fd >= 0) { dir->__d_fd = fd; dir->__fh = this; res = dir; } else { cygheap_fdnew cfd; if (cfd >= 0 && open (O_RDONLY, 0)) { cfd = this; dir->__d_fd = cfd; dir->__fh = this; res = dir; } } close_on_exec (true); } syscall_printf ("%p = opendir (%s)", res, get_name ()); return res; } long fhandler_virtual::telldir (DIR * dir) { return dir->__d_position; } void fhandler_virtual::seekdir (DIR * dir, long loc) { dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot; dir->__d_position = loc; } void fhandler_virtual::rewinddir (DIR * dir) { dir->__d_position = 0; dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot; } int fhandler_virtual::closedir (DIR * dir) { return 0; } off_t fhandler_virtual::lseek (off_t offset, int whence) { /* * On Linux, when you lseek within a /proc file, * the contents of the file are updated. */ if (!fill_filebuf ()) return (off_t) -1; switch (whence) { case SEEK_SET: position = offset; break; case SEEK_CUR: position += offset; break; case SEEK_END: position = filesize + offset; break; default: set_errno (EINVAL); return (off_t) -1; } return position; } int fhandler_virtual::dup (fhandler_base * child, int flags) { int ret = fhandler_base::dup (child, flags); if (!ret) { fhandler_virtual *fhproc_child = (fhandler_virtual *) child; fhproc_child->filebuf = (char *) cmalloc_abort (HEAP_BUF, filesize); memcpy (fhproc_child->filebuf, filebuf, filesize); } return ret; } int fhandler_virtual::close () { if (!have_execed) { if (filebuf) { cfree (filebuf); filebuf = NULL; } } return 0; } void __reg3 fhandler_virtual::read (void *ptr, size_t& len) { if (len == 0) return; if (openflags & O_DIROPEN) { set_errno (EISDIR); len = (size_t) -1; return; } if (!filebuf) { len = (size_t) 0; return; } if ((ssize_t) len > filesize - position) len = (size_t) (filesize - position); if ((ssize_t) len < 0) len = 0; else memcpy (ptr, filebuf + position, len); position += len; } ssize_t __stdcall fhandler_virtual::write (const void *ptr, size_t len) { set_errno (EACCES); return -1; } /* low-level open for all proc files */ int fhandler_virtual::open (int flags, mode_t mode) { rbinary (true); wbinary (true); set_flags ((flags & ~O_TEXT) | O_BINARY); return 1; } virtual_ftype_t fhandler_virtual::exists () { return virt_none; } bool fhandler_virtual::fill_filebuf () { return true; } int fhandler_virtual::fchmod (mode_t mode) { /* Same as on Linux. */ set_errno (EPERM); return -1; } int fhandler_virtual::fchown (uid_t uid, gid_t gid) { /* Same as on Linux. */ set_errno (EPERM); return -1; } int fhandler_virtual::facl (int cmd, int nentries, aclent_t *aclbufp) { int res = fhandler_base::facl (cmd, nentries, aclbufp); if (res >= 0 && cmd == GETACL) { aclbufp[0].a_perm = (S_IRUSR | (pc.isdir () ? S_IXUSR : 0)) >> 6; aclbufp[1].a_perm = (S_IRGRP | (pc.isdir () ? S_IXGRP : 0)) >> 3; aclbufp[2].a_perm = S_IROTH | (pc.isdir () ? S_IXOTH : 0); } return res; } int __reg2 fhandler_virtual::fstatvfs (struct statvfs *sfs) { /* Virtual file system. Just return an empty buffer with a few values set to something useful. Just as on Linux. */ memset (sfs, 0, sizeof (*sfs)); sfs->f_bsize = sfs->f_frsize = 4096; sfs->f_flag = ST_RDONLY; sfs->f_namemax = NAME_MAX; return 0; }