OLD | NEW |
1 /* Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 /* Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 * Use of this source code is governed by a BSD-style license that can be | 2 * Use of this source code is governed by a BSD-style license that can be |
3 * found in the LICENSE file. | 3 * found in the LICENSE file. |
4 */ | 4 */ |
5 | |
6 #include <assert.h> | |
7 #include <dirent.h> | 5 #include <dirent.h> |
8 #include <errno.h> | 6 #include <errno.h> |
9 #include <fcntl.h> | 7 #include <fcntl.h> |
10 #include <sys/stat.h> | 8 #include <sys/stat.h> |
11 #include <string> | 9 #include <string> |
12 | 10 |
13 #include <vector> | 11 #include "mount.h" |
14 | |
15 #include "mount_mem.h" | 12 #include "mount_mem.h" |
| 13 #include "mount_node.h" |
| 14 #include "mount_node_dir.h" |
| 15 #include "mount_node_mem.h" |
16 #include "path.h" | 16 #include "path.h" |
17 #include "util/simple_auto_lock.h" | 17 |
18 | 18 #include "auto_lock.h" |
19 static const size_t s_blksize = 1024; | 19 #include "ref_object.h" |
20 | 20 |
21 class MountNodeMem { | 21 // TODO(noelallen) : Grab/Redefine these in the kernel object once available. |
22 public: | 22 #define USR_ID 1002 |
23 MountNodeMem(int ino) { | 23 #define GRP_ID 1003 |
24 ino_ = ino; | 24 |
25 ref_count_ = 0; | 25 MountMem::MountMem() |
26 data_ = NULL; | 26 : MountFactory(), |
27 memset(&stat_, 0, sizeof(stat_)); | 27 root_(NULL), |
28 Resize(s_blksize); | 28 max_ino_(0) { |
29 }; | 29 } |
30 | 30 |
31 size_t Size() const { | 31 bool MountMem::Init(int dev, StringMap_t& args) { |
32 return stat_.st_size; | 32 dev_ = dev; |
33 } | 33 root_ = AllocatePath(_S_IREAD | _S_IWRITE); |
34 | 34 return (bool) (root_ != NULL); |
35 void Resize(size_t size) { | 35 } |
36 size_t cap = blocks_; | 36 |
37 size_t req = (size + s_blksize - 1) / s_blksize; | 37 void MountMem::Destroy() { |
38 | 38 if (root_) |
39 if ((req > cap) || (req < (cap / 2))) { | 39 root_->Release(); |
40 char *newdata = (char *) malloc(req * s_blksize); | 40 root_ = NULL; |
41 memcpy(newdata, data_, size); | 41 } |
42 free(data_); | 42 |
43 stat_.st_size = size; | 43 MountNode* MountMem::AllocatePath(int mode) { |
44 data_ = newdata; | 44 ino_t ino = AllocateINO(); |
45 blocks_ = req; | 45 |
| 46 MountNode *ptr = new MountNodeDir(this, ino, dev_); |
| 47 if (!ptr->Init(mode, 1002, 1003)) { |
| 48 ptr->Release(); |
| 49 FreeINO(ino); |
| 50 return NULL; |
| 51 } |
| 52 return ptr; |
| 53 } |
| 54 |
| 55 MountNode* MountMem::AllocateData(int mode) { |
| 56 ino_t ino = AllocateINO(); |
| 57 |
| 58 MountNode* ptr = new MountNodeMem(this, ino, dev_); |
| 59 if (!ptr->Init(mode, USR_ID, GRP_ID)) { |
| 60 ptr->Release(); |
| 61 FreeINO(ino); |
| 62 return NULL; |
| 63 } |
| 64 return ptr; |
| 65 } |
| 66 |
| 67 void MountMem::ReleaseNode(MountNode* node) { |
| 68 node->Release(); |
| 69 } |
| 70 |
| 71 int MountMem::AllocateINO() { |
| 72 const int INO_CNT = 8; |
| 73 |
| 74 // If we run out of INO numbers, then allocate 8 more |
| 75 if (inos_.size() == 0) { |
| 76 max_ino_ += INO_CNT; |
| 77 // Add eight more to the stack in reverse order, offset by 1 |
| 78 // since '0' refers to no INO. |
| 79 for (int a = 0; a < INO_CNT; a++) { |
| 80 inos_.push_back((max_ino_ - a) + 1); |
46 } | 81 } |
47 } | 82 } |
48 | 83 |
49 int ino_; | 84 // Return the INO at the top of the stack. |
50 int ref_count_; | 85 int val = inos_.back(); |
51 struct stat stat_; | 86 inos_.pop_back(); |
52 void *data_; | 87 return val; |
53 size_t blocks_; | 88 } |
54 pthread_mutex_t lock_; | 89 |
55 }; | 90 void MountMem::FreeINO(int ino) { |
56 | 91 inos_.push_back(ino); |
57 MountMem::MountMem() {} | 92 } |
58 MountMem::~MountMem() {} | 93 |
59 | 94 MountNode* MountMem::FindNode(const Path& path, int type) { |
60 int MountMem::AddDirEntry(MountMemNode* dir_node, MountMemNode* obj_node, const
char *name) { | 95 MountNode* node = root_; |
61 if (strlen(name > 255)) { | 96 |
| 97 // If there is no root there, we have an error. |
| 98 if (node == NULL) { |
| 99 errno = ENOTDIR; |
| 100 return NULL; |
| 101 } |
| 102 |
| 103 // We are expecting an "absolute" path from this mount point. |
| 104 if (!path.IsAbsolute()) { |
62 errno = EINVAL; | 105 errno = EINVAL; |
63 return -1; | 106 return NULL; |
64 } | 107 } |
65 | 108 |
66 struct dirent* d = (struct dirent *) dir_node->data_; | 109 // Starting at the root, traverse the path parts. |
67 size_t cnt = dir_node->Size() / sizeof(struct dirent); | 110 for (size_t index = 1; node && index < path.Size(); index++) { |
68 size_t off; | 111 // If not a directory, then we have an error so return. |
69 | 112 if (!node->IsaDir()) { |
70 // Find a free location | 113 errno = ENOTDIR; |
71 for (off = 0; off < cnt; off++) { | 114 return NULL; |
72 if (d->d_name[0] == 0) break; | |
73 d++; | |
74 } | |
75 | |
76 // Otherwise regrow and take the last spot. | |
77 if (off == cnt) { | |
78 dir_node->Resize(dir_node->Size() + sizeof(struct dirent)); | |
79 d = &((struct dirent *) dir_node->data_)[off]; | |
80 } | |
81 | |
82 strcpy(d->d_name, name); | |
83 d->d_ino = obj_node->ino_; | |
84 d->d_reclen = sizeof(dirent); | |
85 d->d_off = off * sizeof(dirent); | |
86 | |
87 // Add a ref_count_ and link count for the directory | |
88 obj_node->Acquire(); | |
89 obj_node->stat_.st_nlink++; | |
90 return 0; | |
91 } | |
92 | |
93 | |
94 | |
95 | |
96 int MountMem::DelDirEntry_locked(int dir_ino, const char *name) { | |
97 MountMemNode* dir_node = inodes_.At(dir_ino); | |
98 struct dirent* d = (struct dirent *) dir_node->data_; | |
99 size_t cnt = dir_node->Size() / sizeof(struct dirent); | |
100 size_t off = 0; | |
101 | |
102 // Find a free location | |
103 for (off = 0; off < cnt; off++) { | |
104 if (!strcmp(d->d_name, name)) { | |
105 d->d_name[0] = 0; | |
106 obj_node->stat_.st_nlink--; | |
107 if (0 == --obj_node->ref_count_) FreeNode(obj_node->ino_); | |
108 dir_node->ref_count_--; | |
109 return 0; | |
110 } | 115 } |
111 } | 116 |
112 | 117 // Find the child node |
113 errno = ENOENT; | 118 node = node->FindChild(path.Part(index)); |
114 return -1; | 119 } |
115 } | 120 |
116 | 121 // node should be root, a found child, or a failed 'FindChild' |
117 MountNodeMem* MountMem::AcquireNode(int ino) { | 122 // which already has the correct errno set. |
118 SimpleAutoLock lock(&lock_); | 123 if (NULL == node) return NULL; |
119 MountNodeMem *node = inodes_.At(ino); | 124 |
120 if (node) node->ref_count_++; | 125 // If a directory is expected, but it's not a directory, then fail. |
| 126 if ((type & _S_IFDIR) && !node->IsaDir()) { |
| 127 errno = ENOTDIR; |
| 128 return NULL; |
| 129 } |
| 130 |
| 131 // If a file is expected, but it's not a file, then fail. |
| 132 if ((type & _S_IFREG) && node->IsaDir()) { |
| 133 errno = EISDIR; |
| 134 return NULL; |
| 135 } |
| 136 |
| 137 // We now have a valid object of the expected type, so return it. |
121 return node; | 138 return node; |
122 } | 139 } |
123 | 140 |
124 void MountMem::ReleaseNode(MountMemNode* node) { | 141 MountNode* MountMem::Open(const Path& path, int mode) { |
125 if (node) { | 142 AutoLock lock(&lock_); |
126 SimpleAutoLock lock(&lock_); | 143 MountNode* node = FindNode(path); |
127 if (--node->ref_count_) inodes_.Free(node->ino_); | |
128 } | |
129 } | |
130 | |
131 void MountMem::Init(void) { | |
132 int root = inodes_.Alloc(); | |
133 | |
134 assert(root == 0); | |
135 AddDirEntry(root, root, "/"); | |
136 } | |
137 | |
138 int MountMem::Mkdir(const std::string& path, mode_t mode, struct stat *buf) { | |
139 SimpleAutoLock lock(&lock_); | |
140 | |
141 int ino = AcquireNode(path); | |
142 | |
143 // Make sure it doesn't already exist. | |
144 child = GetMemNode(path); | |
145 if (child) { | |
146 errno = EEXIST; | |
147 return -1; | |
148 } | |
149 // Get the parent node. | |
150 int parent_slot = GetParentSlot(path); | |
151 if (parent_slot == -1) { | |
152 errno = ENOENT; | |
153 return -1; | |
154 } | |
155 parent = slots_.At(parent_slot); | |
156 if (!parent->is_dir()) { | |
157 errno = ENOTDIR; | |
158 return -1; | |
159 } | |
160 | |
161 // Create a new node | |
162 int slot = slots_.Alloc(); | |
163 child = slots_.At(slot); | |
164 child->set_slot(slot); | |
165 child->set_mount(this); | |
166 child->set_is_dir(true); | |
167 Path p(path); | |
168 child->set_name(p.Last()); | |
169 child->set_parent(parent_slot); | |
170 parent->AddChild(slot); | |
171 if (!buf) { | |
172 return 0; | |
173 } | |
174 | |
175 return Stat(slot, buf); | |
176 } | |
177 | |
178 int MountMem::Rmdir(int node) { | |
179 } | |
180 | |
181 int MountMem::Chmod(int ino, int mode) { | |
182 MountMemNode* node = AcquireNode(ino); | |
183 | 144 |
184 if (NULL == node) { | 145 if (NULL == node) { |
185 errno = BADF; | 146 // Now first find the parent directory to see if we can add it |
186 return -1; | 147 MountNode* parent = FindNode(path.Parent(), _S_IFDIR); |
187 } | 148 if (NULL == parent) return NULL; |
188 | 149 |
189 node->stat_.st_mode = mode; | 150 // If the node does not exist and we can't create it, fail |
| 151 if ((mode & O_CREAT) == 0) return NULL; |
| 152 |
| 153 // Otherwise, create it with a single refernece |
| 154 mode = OpenModeToPermission(mode); |
| 155 node = AllocateData(mode); |
| 156 if (NULL == node) return NULL; |
| 157 |
| 158 if (parent->AddChild(path.Filename(), node) == -1) { |
| 159 // Or if it fails, release it |
| 160 node->Release(); |
| 161 return NULL; |
| 162 } |
| 163 return node; |
| 164 } |
| 165 |
| 166 // If we were expected to create it exclusively, fail |
| 167 if (mode & O_EXCL) { |
| 168 errno = EEXIST; |
| 169 return NULL; |
| 170 } |
| 171 |
| 172 // Verify we got the requested permisions. |
| 173 int req_mode = OpenModeToPermission(mode); |
| 174 int obj_mode = node->GetMode() & OpenModeToPermission(O_RDWR); |
| 175 if ((obj_mode & req_mode) != req_mode) { |
| 176 errno = EACCES; |
| 177 return NULL; |
| 178 } |
| 179 |
| 180 // We opened it, so ref count it before passing it back. |
| 181 node->Acquire(); |
| 182 return node; |
| 183 } |
| 184 |
| 185 int MountMem::Close(MountNode* node) { |
| 186 AutoLock lock(&lock_); |
| 187 node->Close(); |
190 ReleaseNode(node); | 188 ReleaseNode(node); |
191 return 0; | 189 return 0; |
192 } | 190 } |
193 | 191 |
194 int MountMem::Stat(int ino, struct stat *buf) { | 192 int MountMem::Unlink(const Path& path) { |
195 MountMemNode* node = AcquireNode(ino); | 193 AutoLock lock(&lock_); |
196 | 194 MountNode* parent = FindNode(path.Parent(), _S_IFDIR); |
| 195 |
| 196 if (NULL == parent) return -1; |
| 197 |
| 198 MountNode* child = parent->FindChild(path.Filename()); |
| 199 if (NULL == child) { |
| 200 errno = ENOENT; |
| 201 return -1; |
| 202 } |
| 203 if (child->IsaDir()) { |
| 204 errno = EISDIR; |
| 205 return -1; |
| 206 } |
| 207 return parent->RemoveChild(path.Filename()); |
| 208 } |
| 209 |
| 210 int MountMem::Mkdir(const Path& path, int mode) { |
| 211 AutoLock lock(&lock_); |
| 212 |
| 213 // We expect a Mount "absolute" path |
| 214 if (!path.IsAbsolute()) { |
| 215 errno = ENOENT; |
| 216 return -1; |
| 217 } |
| 218 |
| 219 // The root of the mount is already created by the mount |
| 220 if (path.Size() == 1) { |
| 221 errno = EEXIST; |
| 222 return -1; |
| 223 } |
| 224 |
| 225 MountNode* parent = FindNode(path.Parent(), _S_IFDIR); |
| 226 MountNode* node; |
| 227 |
| 228 // If we failed to find the parent, the error code is already set. |
| 229 if (NULL == parent) return -1; |
| 230 |
| 231 node = parent->FindChild(path.Filename()); |
| 232 if (NULL != node) { |
| 233 errno = EEXIST; |
| 234 return -1; |
| 235 } |
| 236 |
| 237 // Otherwise, create a new node and attempt to add it |
| 238 mode = OpenModeToPermission(mode); |
| 239 |
| 240 // Allocate a node, with a RefCount of 1. If added to the parent |
| 241 // it will get ref counted again. In either case, release the |
| 242 // recount we have on exit. |
| 243 node = AllocatePath(_S_IREAD | _S_IWRITE); |
| 244 if (NULL == node) return -1; |
| 245 |
| 246 if (parent->AddChild(path.Filename(), node) == -1) { |
| 247 node->Release(); |
| 248 return -1; |
| 249 } |
| 250 |
| 251 node->Release(); |
| 252 return 0; |
| 253 } |
| 254 |
| 255 int MountMem::Rmdir(const Path& path) { |
| 256 AutoLock lock(&lock_); |
| 257 |
| 258 // We expect a Mount "absolute" path |
| 259 if (!path.IsAbsolute()) { |
| 260 errno = ENOENT; |
| 261 return -1; |
| 262 } |
| 263 |
| 264 // The root of the mount is already created by the mount |
| 265 if (path.Size() == 1) { |
| 266 errno = EEXIST; |
| 267 return -1; |
| 268 } |
| 269 |
| 270 MountNode* parent = FindNode(path.Parent(), _S_IFDIR); |
| 271 MountNode* node; |
| 272 |
| 273 // If we failed to find the parent, the error code is already set. |
| 274 if (NULL == parent) return -1; |
| 275 |
| 276 // Verify we find a child which is also a directory |
| 277 node = parent->FindChild(path.Filename()); |
197 if (NULL == node) { | 278 if (NULL == node) { |
198 errno = BADF; | 279 errno = ENOENT; |
199 return -1; | 280 return -1; |
200 } | 281 } |
201 | 282 if (!node->IsaDir()) { |
202 memcpy(buf, node->stat_, sizeof(struct stat)); | 283 errno = ENOTDIR; |
203 ReleaseNode(node); | 284 return -1; |
204 return 0; | 285 } |
205 } | 286 if (node->ChildCount() > 0) { |
206 | 287 errno = ENOTEMPTY; |
207 int MountMem::Fsync(int ino) { | 288 return -1; |
208 // Acquire the node in case he node | 289 } |
209 MountMemNode* node = AcquireNode(ino); | 290 return parent->RemoveChild(path.Filename()); |
210 if (node) { | 291 } |
211 ReleaseNode(node); | |
212 return 0 | |
213 } | |
214 | |
215 errno = BADF; | |
216 return -1; | |
217 } | |
218 | |
219 int MountMem::Getdents(int ino, off_t offset, struct dirent *dirp, unsigned int
count) { | |
220 MountMemNode* node = AcquireNode(ino); | |
221 if ((NULL == node) == 0) { | |
222 errno = EBADF; | |
223 return -1; | |
224 } | |
225 | |
226 if ((node->stat_.st_mode & S_IFDIR) == 0) { | |
227 errno =ENOTDIR; | |
228 return -1; | |
229 } | |
230 | |
231 if (offset + count > node->Size()) { | |
232 count = node->Size() - offset; | |
233 } | |
234 | |
235 memcpy(dirp, &node->data_[offset], count); | |
236 return count; | |
237 } | |
238 | |
239 ssize_t MountMem::Write(int ino, off_t offset, const void *buf, size_t count) { | |
240 MountMemNode* node = AcquireNode(ino); | |
241 | |
242 if (NULL == node || (node->stat_.st_mode & S_IWUSER) == 0) { | |
243 errno = EBADF; | |
244 return -1; | |
245 } | |
246 | |
247 if (offset + count > node->Size()) { | |
248 int err = node->Resize(); | |
249 if (err) { | |
250 errno = err; | |
251 ReleaseNode(node); | |
252 return -1; | |
253 } | |
254 } | |
255 | |
256 mempcy(&node->data_[offset], buf, count); | |
257 ReleaseNode(node); | |
258 return count; | |
259 } | |
260 | |
261 ssize_t MountMem::Read(int ino, off_t offset, const void *buf, size_t count) { | |
262 MountMemNode* node = AcquireNode(ino); | |
263 | |
264 if (NULL == node || (node->stat_.st_mode & S_IRUSER) == 0) { | |
265 errno = EBADF; | |
266 return -1; | |
267 } | |
268 | |
269 if (offset + count > node->Size()) { | |
270 count = node->Size() - offset; | |
271 } | |
272 | |
273 mempcy(buf, &node->data_[offset], count); | |
274 ReleaseNode(node); | |
275 return count; | |
276 } | |
277 | |
278 int MountMem::Isatty(int ino) { | |
279 // Acquire the node in case he node array is in flux | |
280 MountMemNode* node = AcquireNode(ino); | |
281 | |
282 if (node) { | |
283 errno = ENOTTY; | |
284 ReleaseNode(node); | |
285 } | |
286 else { | |
287 errno = BADF; | |
288 } | |
289 return 0; | |
290 } | |
291 | |
OLD | NEW |