| /* cpio.c - Common java file IO native functions |
| Copyright (C) 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| /* do not move; needed here because of some macro definitions */ |
| #include <config.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| |
| #include <jni.h> |
| |
| #if defined(HAVE_SYS_IOCTL_H) |
| #define BSD_COMP /* Get FIONREAD on Solaris2 */ |
| #include <sys/ioctl.h> |
| #endif |
| #if defined(HAVE_SYS_FILIO_H) /* Get FIONREAD on Solaris 2.5 */ |
| #include <sys/filio.h> |
| #endif |
| |
| #if defined(HAVE_SYS_STAT_H) |
| #include <sys/stat.h> |
| #endif |
| |
| #if defined(HAVE_FCNTL_H) |
| #include <fcntl.h> |
| #endif |
| |
| #if defined(HAVE_UNISTD_H) |
| #include <unistd.h> |
| #endif |
| |
| #if defined(HAVE_SYS_SELECT_H) |
| #include <sys/select.h> |
| #endif |
| |
| #if defined(HAVE_STATVFS) |
| #include <sys/statvfs.h> |
| #endif |
| |
| #include <utime.h> |
| |
| #include "cpnative.h" |
| #include "cpio.h" |
| |
| /* Some POSIX systems don't have O_SYNC and O_DYSNC so we define them here. */ |
| #if !defined (O_SYNC) && defined (O_FSYNC) |
| #define O_SYNC O_FSYNC |
| #endif |
| #if !defined (O_DSYNC) && defined (O_FSYNC) |
| #define O_DSYNC O_FSYNC |
| #endif |
| /* If O_DSYNC is still not defined, use O_SYNC (needed for newlib). */ |
| #if !defined (O_DSYNC) |
| #define O_DSYNC O_SYNC |
| #endif |
| |
| JNIEXPORT int cpio_openFile (const char *filename, int *fd, int flags, int permissions) |
| { |
| int sflags = 0; |
| int rwflags = flags & CPFILE_FLAG_READWRITE; |
| int perms; |
| |
| if (flags & CPFILE_FLAG_CREATE) |
| sflags |= O_CREAT; |
| if (flags & CPFILE_FLAG_APPEND) |
| sflags |= O_APPEND; |
| if (flags & CPFILE_FLAG_TRUNCATE) |
| sflags |= O_TRUNC; |
| if (flags & CPFILE_FLAG_SYNC) |
| sflags |= O_SYNC; |
| if (flags & CPFILE_FLAG_DSYNC) |
| sflags |= O_DSYNC; |
| #if defined(O_BINARY) |
| if (flags & CPFILE_FLAG_BINARY) |
| sflags |= O_BINARY; |
| #endif |
| |
| switch (rwflags) |
| { |
| case CPFILE_FLAG_READ: |
| sflags |= O_RDONLY; |
| break; |
| case CPFILE_FLAG_WRITE: |
| sflags |= O_WRONLY; |
| break; |
| case CPFILE_FLAG_READWRITE: |
| sflags |= O_RDWR; |
| break; |
| } |
| |
| if (permissions == CPFILE_PERMISSION_NORMAL) |
| perms = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); |
| else |
| perms = 0; |
| |
| *fd = open (filename, sflags, perms); |
| |
| if (*fd < 0) |
| return errno; |
| |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_closeFile (int fd) |
| { |
| if (close (fd) < 0) |
| return errno; |
| |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_availableBytes (int fd, jlong *bytes_available) |
| { |
| #if defined (FIONREAD) |
| ssize_t n; |
| |
| if (ioctl (fd, FIONREAD, (char *)&n) != 0) |
| return errno; |
| |
| *bytes_available = n; |
| return CPNATIVE_OK; |
| #elif defined(HAVE_FSTAT) |
| struct stat statBuffer; |
| off_t n; |
| int result; |
| |
| *bytes_available = 0; |
| if ((fstat (fd, &statBuffer) == 0) && S_ISREG (statBuffer.st_mode)) |
| { |
| n = lseek (fd, 0, SEEK_CUR); |
| if (n != -1) |
| { |
| *bytes_available = statBuffer.st_size - n; |
| result = CPNATIVE_OK; |
| } |
| else |
| { |
| result = errno; |
| } |
| } |
| else |
| { |
| result = errno; |
| } |
| |
| return result; |
| #elif defined(HAVE_SELECT) |
| fd_set filedescriptset; |
| struct timeval tv; |
| int result; |
| |
| *bytes_available = 0; |
| |
| FD_ZERO (&filedescriptset); |
| FD_SET (fd,&filedescriptset); |
| memset (&tv, 0, sizeof(tv)); |
| |
| switch (select (fd+1, &filedescriptset, NULL, NULL, &tv)) |
| { |
| case -1: |
| result=errno; |
| break; |
| case 0: |
| *bytes_available = 0; |
| result = CPNATIVE_OK; |
| break; |
| default: |
| *bytes_available = 1; |
| result = CPNATIVE_OK; |
| break; |
| } |
| return result; |
| |
| #else |
| *bytes_available = 0; |
| return ENOTSUP; |
| #endif |
| } |
| |
| JNIEXPORT int cpio_getFileSize (int fd, jlong *filesize) |
| { |
| struct stat statBuffer; |
| |
| if (fstat(fd, &statBuffer) < 0) |
| return errno; |
| |
| *filesize = statBuffer.st_size; |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_getFilePosition (int fd, jlong *offset) |
| { |
| *offset = lseek (fd, 0, SEEK_CUR); |
| if (*offset < 0) |
| return errno; |
| |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_setFilePosition (int fd, jlong position) |
| { |
| if (lseek (fd, position, SEEK_SET) < 0) |
| return errno; |
| |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_read (int fd, void *buffer, jint length, jint *bytes_read) |
| { |
| *bytes_read = read (fd, buffer, length); |
| |
| if (*bytes_read < 0) |
| { |
| return errno; |
| } |
| |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_write (int fd, const void *buffer, jint length, jint *bytes_written) |
| { |
| *bytes_written = write (fd, buffer, length); |
| |
| if (*bytes_written < 0) |
| return errno; |
| |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_fsync (int fd) |
| { |
| if (fsync (fd) < 0) |
| return errno; |
| |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_truncate (int fd, jlong size) |
| { |
| if (ftruncate (fd, size) < 0) |
| return errno; |
| |
| return CPNATIVE_OK; |
| } |
| |
| JNIEXPORT int cpio_setFileSize (int native_fd, jlong new_size) |
| { |
| jlong file_size; |
| jlong save_offset; |
| int result; |
| char data; |
| jint bytes_written; |
| |
| result = cpio_getFileSize (native_fd, &file_size); |
| if (result != CPNATIVE_OK) |
| return result; |
| |
| /* Save off current position */ |
| result = cpio_getFilePosition (native_fd, &save_offset); |
| if (result != CPNATIVE_OK) |
| return result; |
| |
| if (file_size < new_size) |
| { |
| /* File is too short -- seek to one byte short of where we want, |
| * then write a byte */ |
| |
| /* move to position n-1 */ |
| result = cpio_setFilePosition (native_fd, new_size-1); |
| if (result != CPNATIVE_OK) |
| return result; |
| |
| /* write a byte |
| Note: This will fail if we somehow get here in read only mode |
| * That shouldn't happen */ |
| data = '\0'; |
| result = cpio_write (native_fd, &data, 1, &bytes_written); |
| if (result != CPNATIVE_OK) |
| return result; |
| |
| /* Reposition file pointer to where we started if not beyond new len. */ |
| if (save_offset < new_size) |
| { |
| result = cpio_setFilePosition (native_fd, save_offset); |
| if (result != CPNATIVE_OK) |
| return result; |
| } |
| } |
| else if (new_size < file_size) |
| { |
| /* File is too long - use ftruncate if available */ |
| result = cpio_truncate (native_fd, new_size); |
| if (result != CPNATIVE_OK) |
| return result; |
| |
| /* Reposition file pointer when it now is beyond the end of file. */ |
| if (new_size < save_offset) |
| { |
| result = cpio_setFilePosition (native_fd, new_size); |
| if (result != CPNATIVE_OK) |
| return result; |
| } |
| } |
| |
| return CPNATIVE_OK; |
| } |
| |
| int cpio_setFileReadonly (const char *filename) |
| { |
| struct stat statbuf; |
| |
| if (stat(filename, &statbuf) < 0) |
| return errno; |
| |
| #ifdef S_IWRITE |
| if (chmod(filename, statbuf.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)) < 0) |
| return errno; |
| #endif |
| |
| return 0; |
| } |
| |
| int cpio_chmod (const char *filename, int permissions) |
| { |
| struct stat statbuf; |
| int perms = 0; |
| |
| if (stat(filename, &statbuf) < 0) |
| return errno; |
| |
| /* check for permission flags */ |
| if (permissions & CPFILE_FLAG_USR) |
| { |
| if (permissions & CPFILE_FLAG_READ) |
| perms |= S_IRUSR; |
| |
| if (permissions & CPFILE_FLAG_WRITE) |
| perms |= S_IWUSR; |
| |
| if (permissions & CPFILE_FLAG_EXEC) |
| perms |= S_IXUSR; |
| } |
| else |
| { |
| if (permissions & CPFILE_FLAG_READ) |
| perms |= (S_IRUSR | S_IRGRP | S_IROTH); |
| |
| if (permissions & CPFILE_FLAG_WRITE) |
| perms |= (S_IWUSR | S_IWGRP | S_IWOTH); |
| |
| if (permissions & CPFILE_FLAG_EXEC) |
| perms |= (S_IXUSR | S_IXGRP | S_IXOTH); |
| } |
| |
| if (permissions & CPFILE_FLAG_OFF) |
| perms = statbuf.st_mode & ~perms; |
| else |
| perms = statbuf.st_mode | perms; |
| |
| if (chmod(filename, perms) < 0) |
| return errno; |
| |
| return 0; |
| } |
| |
| JNIEXPORT long long |
| cpio_df (__attribute__((unused)) const char *path, |
| __attribute__((unused)) CPFILE_DF_TYPE type) |
| { |
| long long result = 0L; |
| |
| #if defined(HAVE_STATVFS) |
| |
| long long scale_factor = 0L; |
| struct statvfs buf; |
| |
| if (statvfs (path, &buf) < 0) |
| return 0L; |
| |
| /* f_blocks, f_bfree and f_bavail are defined in terms of f_frsize */ |
| scale_factor = (long long) (buf.f_frsize); |
| |
| switch (type) |
| { |
| case TOTAL: |
| result = (long long) (buf.f_blocks * scale_factor); |
| break; |
| case FREE: |
| result = (long long) (buf.f_bfree * scale_factor); |
| break; |
| case USABLE: |
| result = (long long) (buf.f_bavail * scale_factor); |
| break; |
| default: |
| result = 0L; |
| break; |
| } |
| |
| #endif |
| |
| return result; |
| } |
| |
| int cpio_checkAccess (const char *filename, unsigned int flag) |
| { |
| struct stat statbuf; |
| unsigned int perms = 0; |
| |
| if (stat(filename, &statbuf) < 0) |
| return errno; |
| |
| switch (flag) |
| { |
| case CPFILE_FLAG_READ: |
| perms = R_OK; |
| break; |
| |
| case CPFILE_FLAG_WRITE: |
| perms = W_OK; |
| break; |
| |
| case CPFILE_FLAG_EXEC: |
| default: |
| perms = X_OK; |
| break; |
| } |
| |
| return (access (filename, perms)); |
| } |
| |
| int cpio_isFileExists (const char *filename) |
| { |
| struct stat statbuf; |
| |
| if (stat(filename, &statbuf) < 0) |
| { |
| return errno; |
| } |
| |
| return 0; |
| } |
| |
| int cpio_checkType (const char *filename, jint *entryType) |
| { |
| struct stat statbuf; |
| |
| if (stat(filename, &statbuf) < 0) |
| return errno; |
| |
| if (S_ISDIR(statbuf.st_mode)) |
| *entryType = CPFILE_DIRECTORY; |
| else |
| *entryType = CPFILE_FILE; |
| |
| return 0; |
| } |
| |
| int cpio_getModificationTime (const char *filename, jlong *mtime) |
| { |
| struct stat statbuf; |
| |
| if (stat(filename, &statbuf) < 0) |
| return errno; |
| |
| *mtime = (jlong)statbuf.st_mtime * (jlong)1000; |
| |
| return 0; |
| } |
| |
| int cpio_setModificationTime (const char *filename, jlong mtime) |
| { |
| struct stat statbuf; |
| struct utimbuf buf; |
| |
| if (stat(filename, &statbuf) < 0) |
| return errno; |
| |
| buf.actime = statbuf.st_atime; |
| buf.modtime = mtime / 1000; |
| |
| if (utime(filename, &buf) < 0) |
| return errno; |
| |
| return 0; |
| } |
| |
| int cpio_removeFile (const char *filename) |
| { |
| if (unlink(filename) < 0 && rmdir(filename) < 0) |
| return errno; |
| |
| return 0; |
| } |
| |
| int cpio_mkdir (const char *path) |
| { |
| if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) |
| return errno; |
| |
| return 0; |
| } |
| |
| int cpio_rename (const char *old_name, const char *new_name) |
| { |
| if (rename(old_name, new_name) < 0) |
| return errno; |
| |
| return 0; |
| } |
| |
| int cpio_openDir (const char *dirname, void **handle) |
| { |
| *handle = (void *)opendir(dirname); |
| if (*handle == NULL) |
| return errno; |
| |
| return 0; |
| } |
| |
| int cpio_closeDir (void *handle) |
| { |
| closedir((DIR *)handle); |
| return 0; |
| } |
| |
| |
| int cpio_readDir (void *handle, char *filename) |
| { |
| struct dirent *dBuf; |
| |
| errno = 0; |
| dBuf = readdir((DIR *)handle); |
| |
| if (dBuf == NULL) |
| { |
| /* Some OS's (OS X) return NULL on end-of-dir, but |
| don't set errno to anything. */ |
| if (errno == 0) |
| return ENOENT; /* Whatever. */ |
| return errno; |
| } |
| |
| strncpy (filename, dBuf->d_name, FILENAME_MAX - 1); |
| return 0; |
| } |
| |
| int |
| cpio_closeOnExec(int fd) |
| { |
| if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) |
| return errno; |
| |
| return 0; |
| } |