Add file handle class (a simple file wrapper)
diff --git a/lib/bcc/FileHandle.cpp b/lib/bcc/FileHandle.cpp
new file mode 100644
index 0000000..c4c3954
--- /dev/null
+++ b/lib/bcc/FileHandle.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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