| /* |
| * Copyright 2010, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "bcc" |
| #include <cutils/log.h> |
| |
| #include "FileHandle.h" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/file.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <string.h> |
| |
| namespace bcc { |
| |
| int FileHandle::open(char const *filename, OpenMode::ModeType mode) { |
| static int const open_flags[2] = { |
| O_RDONLY, |
| O_RDWR | O_CREAT | O_TRUNC, |
| }; |
| |
| static int const lock_flags[2] = { LOCK_SH, LOCK_EX }; |
| |
| static char const *const open_mode_str[2] = { "read", "write" }; |
| |
| static size_t const RETRY_MAX = 4; |
| |
| static useconds_t const RETRY_USEC = 200000UL; |
| |
| for (size_t i = 0; i < RETRY_MAX; ++i) { |
| // Try to open the file |
| mFD = ::open(filename, open_flags[mode], 0644); |
| |
| if (mFD < 0) { |
| if (errno == EINTR) { |
| // Interrupt occurs while opening the file. Retry. |
| continue; |
| } |
| |
| LOGE("Unable to open %s in %s mode. (reason: %s)\n", |
| filename, open_mode_str[mode], strerror(errno)); |
| |
| return -1; |
| } |
| |
| // Try to lock the file |
| if (flock(mFD, lock_flags[mode] | LOCK_NB) < 0) { |
| LOGW("Unable to acquire the lock immediately, block and wait now ...\n"); |
| |
| if (flock(mFD, lock_flags[mode]) < 0) { |
| LOGE("Unable to acquire the lock. Retry ...\n"); |
| |
| ::close(mFD); |
| mFD = -1; |
| |
| usleep(RETRY_USEC); |
| continue; |
| } |
| } |
| |
| // Note: From now on, the object is correctly initialized. We have to |
| // use this->close() to close the file now. |
| |
| // Check rather we have locked the correct file or not |
| struct stat sfd, sfname; |
| |
| if (fstat(mFD, &sfd) == -1 || stat(filename, &sfname) == -1 || |
| sfd.st_dev != sfname.st_dev || sfd.st_ino != sfname.st_ino) { |
| // The file we locked is different from the given path. This may |
| // occur when someone changes the file node before we lock the file. |
| // Just close the file, and retry after sleeping. |
| |
| this->close(); |
| usleep(RETRY_USEC); |
| continue; |
| } |
| |
| // Good, we have open and lock the file correctly. |
| return mFD; |
| } |
| |
| LOGE("Unable to open %s in %s mode.\n", filename, open_mode_str[mode]); |
| return -1; |
| } |
| |
| |
| void FileHandle::close() { |
| if (mFD >= 0) { |
| ::close(mFD); |
| flock(mFD, LOCK_UN); |
| mFD = -1; |
| } |
| } |
| |
| |
| ssize_t FileHandle::read(char *buf, size_t count) { |
| if (mFD < 0) { |
| return -1; |
| } |
| |
| while (true) { |
| ssize_t nread = ::read(mFD, static_cast<void *>(buf), count); |
| |
| if (nread >= 0) { |
| return nread; |
| } |
| |
| if (errno != EAGAIN && errno != EINTR) { |
| // If the errno is EAGAIN or EINTR, then we try to read again. |
| // Otherwise, consider this is a failure. And returns zero. |
| return -1; |
| } |
| } |
| |
| // Unreachable |
| return -1; |
| } |
| |
| |
| ssize_t FileHandle::write(char const *buf, size_t count) { |
| if (mFD < 0) { |
| return -1; |
| } |
| |
| ssize_t written = 0; |
| |
| while (count > 0) { |
| ssize_t nwrite = ::write(mFD, static_cast<void const *>(buf), count); |
| |
| if (nwrite < 0) { |
| if (errno != EAGAIN && errno != EINTR) { |
| return written; |
| } |
| |
| continue; |
| } |
| |
| written += nwrite; |
| count -= (size_t)nwrite; |
| buf += (size_t)nwrite; |
| } |
| |
| return written; |
| } |
| |
| |
| off_t FileHandle::seek(off_t offset, int whence) { |
| return (mFD < 0) ? -1 : lseek(mFD, offset, whence); |
| } |
| |
| |
| void FileHandle::truncate() { |
| if (mFD >= 0) { |
| ftruncate(mFD, 0); |
| } |
| } |
| |
| |
| } // namespace bcc |