blob: e19d0d1eb0764df55a919b5a1101728b3a2f490a [file] [log] [blame]
Loganef9e4f72011-01-06 03:18:12 +08001/*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "bcc"
18#include <cutils/log.h>
19
20#include "FileHandle.h"
21
22#include <errno.h>
23#include <fcntl.h>
24#include <sys/file.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <unistd.h>
28
29#include <string.h>
30
31namespace bcc {
32
33int FileHandle::open(char const *filename, OpenMode::ModeType mode) {
34 static int const open_flags[2] = {
35 O_RDONLY,
36 O_RDWR | O_CREAT | O_TRUNC,
37 };
38
39 static int const lock_flags[2] = { LOCK_SH, LOCK_EX };
40
41 static char const *const open_mode_str[2] = { "read", "write" };
42
43 static size_t const RETRY_MAX = 4;
44
45 static useconds_t const RETRY_USEC = 200000UL;
46
47 for (size_t i = 0; i < RETRY_MAX; ++i) {
48 // Try to open the file
49 mFD = ::open(filename, open_flags[mode], 0644);
50
51 if (mFD < 0) {
52 if (errno == EINTR) {
53 // Interrupt occurs while opening the file. Retry.
54 continue;
55 }
56
57 LOGE("Unable to open %s in %s mode. (reason: %s)\n",
58 filename, open_mode_str[mode], strerror(errno));
59
60 return -1;
61 }
62
63 // Try to lock the file
64 if (flock(mFD, lock_flags[mode] | LOCK_NB) < 0) {
65 LOGW("Unable to acquire the lock immediately, block and wait now ...\n");
66
67 if (flock(mFD, lock_flags[mode]) < 0) {
68 LOGE("Unable to acquire the lock. Retry ...\n");
69
70 ::close(mFD);
71 mFD = -1;
72
73 usleep(RETRY_USEC);
74 continue;
75 }
76 }
77
78 // Note: From now on, the object is correctly initialized. We have to
79 // use this->close() to close the file now.
80
81 // Check rather we have locked the correct file or not
82 struct stat sfd, sfname;
83
84 if (fstat(mFD, &sfd) == -1 || stat(filename, &sfname) == -1 ||
85 sfd.st_dev != sfname.st_dev || sfd.st_ino != sfname.st_ino) {
86 // The file we locked is different from the given path. This may
87 // occur when someone changes the file node before we lock the file.
88 // Just close the file, and retry after sleeping.
89
90 this->close();
91 usleep(RETRY_USEC);
92 continue;
93 }
94
95 // Good, we have open and lock the file correctly.
96 return mFD;
97 }
98
99 LOGE("Unable to open %s in %s mode.\n", filename, open_mode_str[mode]);
100 return -1;
101}
102
103
104void FileHandle::close() {
105 if (mFD >= 0) {
106 ::close(mFD);
107 flock(mFD, LOCK_UN);
108 mFD = -1;
109 }
110}
111
112
113ssize_t FileHandle::read(char *buf, size_t count) {
114 if (mFD < 0) {
115 return -1;
116 }
117
118 while (true) {
119 ssize_t nread = ::read(mFD, static_cast<void *>(buf), count);
120
121 if (nread >= 0) {
122 return nread;
123 }
124
125 if (errno != EAGAIN && errno != EINTR) {
126 // If the errno is EAGAIN or EINTR, then we try to read again.
127 // Otherwise, consider this is a failure. And returns zero.
128 return -1;
129 }
130 }
131
132 // Unreachable
133 return -1;
134}
135
136
137ssize_t FileHandle::write(char const *buf, size_t count) {
138 if (mFD < 0) {
139 return -1;
140 }
141
142 ssize_t written = 0;
143
144 while (count > 0) {
145 ssize_t nwrite = ::write(mFD, static_cast<void const *>(buf), count);
146
147 if (nwrite < 0) {
148 if (errno != EAGAIN && errno != EINTR) {
149 return written;
150 }
151
152 continue;
153 }
154
155 written += nwrite;
156 count -= (size_t)nwrite;
157 buf += (size_t)nwrite;
158 }
159
160 return written;
161}
162
163
164off_t FileHandle::seek(off_t offset, int whence) {
165 return (mFD < 0) ? -1 : lseek(mFD, offset, whence);
166}
167
168
169void FileHandle::truncate() {
170 if (mFD >= 0) {
Logancc6da3f2011-01-07 10:45:52 +0800171 if (ftruncate(mFD, 0) != 0) {
172 LOGE("Unable to truncate the file.\n");
173 }
Loganef9e4f72011-01-06 03:18:12 +0800174 }
175}
176
177
178} // namespace bcc