blob: c912c66a303b3d80a69be71466a310bb14a437a9 [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
Logan Chien08f1f1c2011-07-14 19:47:59 +080040#if USE_LOGGER
Loganef9e4f72011-01-06 03:18:12 +080041 static char const *const open_mode_str[2] = { "read", "write" };
Logan Chien08f1f1c2011-07-14 19:47:59 +080042#endif
Loganef9e4f72011-01-06 03:18:12 +080043
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
Steve Block10c51452012-01-05 23:23:07 +000058 ALOGW("Unable to open %s in %s mode. (reason: %s)\n",
Loganef9e4f72011-01-06 03:18:12 +080059 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) {
Steve Block10c51452012-01-05 23:23:07 +000066 ALOGW("Unable to acquire the lock immediately, block and wait now ...\n");
Loganef9e4f72011-01-06 03:18:12 +080067
68 if (flock(mFD, lock_flags[mode]) < 0) {
69 LOGE("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.
Steve Block7ef2cf42011-10-20 11:56:10 +010097 ALOGV("File opened. fd=%d\n", mFD);
Loganef9e4f72011-01-06 03:18:12 +080098 return mFD;
99 }
100
Steve Block10c51452012-01-05 23:23:07 +0000101 ALOGW("Unable to open %s in %s mode.\n", filename, open_mode_str[mode]);
Loganef9e4f72011-01-06 03:18:12 +0800102 return -1;
103}
104
105
106void FileHandle::close() {
107 if (mFD >= 0) {
Loganef9e4f72011-01-06 03:18:12 +0800108 flock(mFD, LOCK_UN);
Logan269df462011-01-07 12:51:48 +0800109 ::close(mFD);
Steve Block7ef2cf42011-10-20 11:56:10 +0100110 ALOGV("File closed. fd=%d\n", mFD);
Loganef9e4f72011-01-06 03:18:12 +0800111 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 }
Shih-wei Liaof7cfc022011-01-07 06:39:53 -0800157
Loganef9e4f72011-01-06 03:18:12 +0800158 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) {
Logancc6da3f2011-01-07 10:45:52 +0800174 if (ftruncate(mFD, 0) != 0) {
175 LOGE("Unable to truncate the file.\n");
176 }
Loganef9e4f72011-01-06 03:18:12 +0800177 }
178}
179
180
181} // namespace bcc