blob: c0a344c83f505ebad39fa6ac7a960734a58d844a [file] [log] [blame]
Stephen Hines758d00c2012-05-03 12:30:15 -07001/*
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#include "FileHandle.h"
18
19#include "DebugHelper.h"
20
21#include <errno.h>
22#include <fcntl.h>
23#include <sys/file.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <unistd.h>
27
28#include <string.h>
29
30namespace bcc {
31
32int FileHandle::open(char const *filename, OpenMode::ModeType mode) {
33 static int const open_flags[2] = {
34 O_RDONLY,
35 O_RDWR | O_CREAT | O_TRUNC,
36 };
37
38 static int const lock_flags[2] = { LOCK_SH, LOCK_EX };
39
Stephen Hines802d1382012-05-03 12:31:09 -070040#if USE_LOGGER
Stephen Hines758d00c2012-05-03 12:30:15 -070041 static char const *const open_mode_str[2] = { "read", "write" };
Stephen Hines802d1382012-05-03 12:31:09 -070042#endif
Stephen Hines758d00c2012-05-03 12:30:15 -070043
44 static size_t const RETRY_MAX = 4;
45
46 static useconds_t const RETRY_USEC = 200000UL;
47
48 for (size_t i = 0; i < RETRY_MAX; ++i) {
49 // Try to open the file
50 mFD = ::open(filename, open_flags[mode], 0644);
51
52 if (mFD < 0) {
53 if (errno == EINTR) {
54 // Interrupt occurs while opening the file. Retry.
55 continue;
56 }
57
58 ALOGW("Unable to open %s in %s mode. (reason: %s)\n",
59 filename, open_mode_str[mode], strerror(errno));
60
61 return -1;
62 }
63
64 // Try to lock the file
65 if (flock(mFD, lock_flags[mode] | LOCK_NB) < 0) {
66 ALOGW("Unable to acquire the lock immediately, block and wait now ...\n");
67
68 if (flock(mFD, lock_flags[mode]) < 0) {
69 ALOGE("Unable to acquire the lock. Retry ...\n");
70
71 ::close(mFD);
72 mFD = -1;
73
74 usleep(RETRY_USEC);
75 continue;
76 }
77 }
78
79 // Note: From now on, the object is correctly initialized. We have to
80 // use this->close() to close the file now.
81
82 // Check rather we have locked the correct file or not
83 struct stat sfd, sfname;
84
85 if (fstat(mFD, &sfd) == -1 || stat(filename, &sfname) == -1 ||
86 sfd.st_dev != sfname.st_dev || sfd.st_ino != sfname.st_ino) {
87 // The file we locked is different from the given path. This may
88 // occur when someone changes the file node before we lock the file.
89 // Just close the file, and retry after sleeping.
90
91 this->close();
92 usleep(RETRY_USEC);
93 continue;
94 }
95
96 // Good, we have open and lock the file correctly.
97 ALOGV("File opened. fd=%d\n", mFD);
98 return mFD;
99 }
100
101 ALOGW("Unable to open %s in %s mode.\n", filename, open_mode_str[mode]);
102 return -1;
103}
104
105
106void FileHandle::close() {
107 if (mFD >= 0) {
108 flock(mFD, LOCK_UN);
109 ::close(mFD);
110 ALOGV("File closed. fd=%d\n", mFD);
111 mFD = -1;
112 }
113}
114
115
116ssize_t FileHandle::read(char *buf, size_t count) {
117 if (mFD < 0) {
118 return -1;
119 }
120
121 while (true) {
122 ssize_t nread = ::read(mFD, static_cast<void *>(buf), count);
123
124 if (nread >= 0) {
125 return nread;
126 }
127
128 if (errno != EAGAIN && errno != EINTR) {
129 // If the errno is EAGAIN or EINTR, then we try to read again.
130 // Otherwise, consider this is a failure. And returns zero.
131 return -1;
132 }
133 }
134
135 // Unreachable
136 return -1;
137}
138
139
140ssize_t FileHandle::write(char const *buf, size_t count) {
141 if (mFD < 0) {
142 return -1;
143 }
144
145 ssize_t written = 0;
146
147 while (count > 0) {
148 ssize_t nwrite = ::write(mFD, static_cast<void const *>(buf), count);
149
150 if (nwrite < 0) {
151 if (errno != EAGAIN && errno != EINTR) {
152 return written;
153 }
154
155 continue;
156 }
157
158 written += nwrite;
159 count -= (size_t)nwrite;
160 buf += (size_t)nwrite;
161 }
162
163 return written;
164}
165
166
167off_t FileHandle::seek(off_t offset, int whence) {
168 return (mFD < 0) ? -1 : lseek(mFD, offset, whence);
169}
170
171
172void FileHandle::truncate() {
173 if (mFD >= 0) {
174 if (ftruncate(mFD, 0) != 0) {
175 ALOGE("Unable to truncate the file.\n");
176 }
177 }
178}
179
180
181} // namespace bcc