Add file handle class (a simple file wrapper)
diff --git a/Android.mk b/Android.mk
index 1559256..88b4570 100644
--- a/Android.mk
+++ b/Android.mk
@@ -31,6 +31,7 @@
   lib/bcc/CodeMemoryManager.cpp \
   lib/bcc/Compiler.cpp \
   lib/bcc/ContextManager.cpp \
+  lib/bcc/FileHandle.cpp \
   lib/bcc/Runtime.c \
   lib/bcc/Script.cpp \
   helper/sha1.c
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
diff --git a/lib/bcc/FileHandle.h b/lib/bcc/FileHandle.h
new file mode 100644
index 0000000..e420f68
--- /dev/null
+++ b/lib/bcc/FileHandle.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef BCC_FILEHANDLE_H
+#define BCC_FILEHANDLE_H
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace bcc {
+  namespace OpenMode {
+    enum ModeType {
+      READ,
+      WRITE
+    };
+  }
+
+  class FileHandle {
+  private:
+    int mFD;
+
+  public:
+    FileHandle() : mFD(-1) {
+    }
+
+    ~FileHandle() {
+      if (mFD >= 0) {
+        close();
+      }
+    }
+
+    int open(char const *filename, OpenMode::ModeType mode);
+
+    void close();
+
+    int getFD() {
+      // Note: This function is designed not being qualified by const.
+      // Because once the file descriptor is given, the user can do every
+      // thing on file descriptor.
+
+      return mFD;
+    }
+
+    off_t seek(off_t offset, int whence);
+
+    ssize_t read(char *buf, size_t count);
+
+    ssize_t write(char const *buf, size_t count);
+
+    void truncate();
+
+  };
+
+} // namespace bcc
+
+#endif // BCC_FILEHANDLE_H