blob: 919cb4881e758cc060079f64e22b131822f88cc4 [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
Loganef9e4f72011-01-06 03:18:12 +080017#include "FileHandle.h"
18
Logan4dcd6792011-02-28 05:12:00 +080019#include "DebugHelper.h"
20
Loganef9e4f72011-01-06 03:18:12 +080021#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
40 static char const *const open_mode_str[2] = { "read", "write" };
41
42 static size_t const RETRY_MAX = 4;
43
44 static useconds_t const RETRY_USEC = 200000UL;
45
46 for (size_t i = 0; i < RETRY_MAX; ++i) {
47 // Try to open the file
48 mFD = ::open(filename, open_flags[mode], 0644);
49
50 if (mFD < 0) {
51 if (errno == EINTR) {
52 // Interrupt occurs while opening the file. Retry.
53 continue;
54 }
55
Shih-wei Liao39ebe2c2011-01-28 11:22:32 -080056 LOGW("Unable to open %s in %s mode. (reason: %s)\n",
Loganef9e4f72011-01-06 03:18:12 +080057 filename, open_mode_str[mode], strerror(errno));
58
59 return -1;
60 }
61
62 // Try to lock the file
63 if (flock(mFD, lock_flags[mode] | LOCK_NB) < 0) {
64 LOGW("Unable to acquire the lock immediately, block and wait now ...\n");
65
66 if (flock(mFD, lock_flags[mode]) < 0) {
67 LOGE("Unable to acquire the lock. Retry ...\n");
68
69 ::close(mFD);
70 mFD = -1;
71
72 usleep(RETRY_USEC);
73 continue;
74 }
75 }
76
77 // Note: From now on, the object is correctly initialized. We have to
78 // use this->close() to close the file now.
79
80 // Check rather we have locked the correct file or not
81 struct stat sfd, sfname;
82
83 if (fstat(mFD, &sfd) == -1 || stat(filename, &sfname) == -1 ||
84 sfd.st_dev != sfname.st_dev || sfd.st_ino != sfname.st_ino) {
85 // The file we locked is different from the given path. This may
86 // occur when someone changes the file node before we lock the file.
87 // Just close the file, and retry after sleeping.
88
89 this->close();
90 usleep(RETRY_USEC);
91 continue;
92 }
93
94 // Good, we have open and lock the file correctly.
Shih-wei Liao39ebe2c2011-01-28 11:22:32 -080095 LOGV("File opened. fd=%d\n", mFD);
Loganef9e4f72011-01-06 03:18:12 +080096 return mFD;
97 }
98
Shih-wei Liao39ebe2c2011-01-28 11:22:32 -080099 LOGW("Unable to open %s in %s mode.\n", filename, open_mode_str[mode]);
Loganef9e4f72011-01-06 03:18:12 +0800100 return -1;
101}
102
103
104void FileHandle::close() {
105 if (mFD >= 0) {
Loganef9e4f72011-01-06 03:18:12 +0800106 flock(mFD, LOCK_UN);
Logan269df462011-01-07 12:51:48 +0800107 ::close(mFD);
Shih-wei Liao39ebe2c2011-01-28 11:22:32 -0800108 LOGV("File closed. fd=%d\n", mFD);
Loganef9e4f72011-01-06 03:18:12 +0800109 mFD = -1;
110 }
111}
112
113
114ssize_t FileHandle::read(char *buf, size_t count) {
115 if (mFD < 0) {
116 return -1;
117 }
118
119 while (true) {
120 ssize_t nread = ::read(mFD, static_cast<void *>(buf), count);
121
122 if (nread >= 0) {
123 return nread;
124 }
125
126 if (errno != EAGAIN && errno != EINTR) {
127 // If the errno is EAGAIN or EINTR, then we try to read again.
128 // Otherwise, consider this is a failure. And returns zero.
129 return -1;
130 }
131 }
132
133 // Unreachable
134 return -1;
135}
136
137
138ssize_t FileHandle::write(char const *buf, size_t count) {
139 if (mFD < 0) {
140 return -1;
141 }
142
143 ssize_t written = 0;
144
145 while (count > 0) {
146 ssize_t nwrite = ::write(mFD, static_cast<void const *>(buf), count);
147
148 if (nwrite < 0) {
149 if (errno != EAGAIN && errno != EINTR) {
150 return written;
151 }
152
153 continue;
154 }
Shih-wei Liaof7cfc022011-01-07 06:39:53 -0800155
Loganef9e4f72011-01-06 03:18:12 +0800156 written += nwrite;
157 count -= (size_t)nwrite;
158 buf += (size_t)nwrite;
159 }
160
161 return written;
162}
163
164
165off_t FileHandle::seek(off_t offset, int whence) {
166 return (mFD < 0) ? -1 : lseek(mFD, offset, whence);
167}
168
169
170void FileHandle::truncate() {
171 if (mFD >= 0) {
Logancc6da3f2011-01-07 10:45:52 +0800172 if (ftruncate(mFD, 0) != 0) {
173 LOGE("Unable to truncate the file.\n");
174 }
Loganef9e4f72011-01-06 03:18:12 +0800175 }
176}
177
178
179} // namespace bcc