• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
30 namespace bcc {
31 
open(char const * filename,OpenMode::ModeType mode)32 int 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 #if USE_LOGGER
41   static char const *const open_mode_str[2] = { "read", "write" };
42 #endif
43 
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       LOGW("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       LOGW("Unable to acquire the lock immediately, block and wait now ...\n");
67 
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.
97     LOGV("File opened. fd=%d\n", mFD);
98     return mFD;
99   }
100 
101   LOGW("Unable to open %s in %s mode.\n", filename, open_mode_str[mode]);
102   return -1;
103 }
104 
105 
close()106 void FileHandle::close() {
107   if (mFD >= 0) {
108     flock(mFD, LOCK_UN);
109     ::close(mFD);
110     LOGV("File closed. fd=%d\n", mFD);
111     mFD = -1;
112   }
113 }
114 
115 
read(char * buf,size_t count)116 ssize_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 
write(char const * buf,size_t count)140 ssize_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 
seek(off_t offset,int whence)167 off_t FileHandle::seek(off_t offset, int whence) {
168   return (mFD < 0) ? -1 : lseek(mFD, offset, whence);
169 }
170 
171 
truncate()172 void FileHandle::truncate() {
173   if (mFD >= 0) {
174     if (ftruncate(mFD, 0) != 0) {
175       LOGE("Unable to truncate the file.\n");
176     }
177   }
178 }
179 
180 
181 } // namespace bcc
182