Chromium Code Reviews| 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 MountMem::MountMem(int dev) : |
|
binji
2012/04/16 22:43:05
nit: move : to next line, indent 4
noelallen1
2012/04/21 18:09:53
Done.
| |
| 22 public: | 22 Mount(dev), |
| 23 MountNodeMem(int ino) { | 23 root_(NULL), |
| 24 ino_ = ino; | 24 max_ino_(0) { |
| 25 ref_count_ = 0; | 25 } |
| 26 data_ = NULL; | 26 |
| 27 memset(&stat_, 0, sizeof(stat_)); | 27 bool MountMem::Init(const std::string& args) { |
| 28 Resize(s_blksize); | 28 root_ = AllocatePath(_S_IREAD | _S_IWRITE); |
| 29 }; | 29 return (bool) (root_ != NULL); |
| 30 | 30 } |
| 31 size_t Size() const { | 31 |
| 32 return stat_.st_size; | 32 void MountMem::Destroy() { |
| 33 } | 33 if (root_) |
| 34 | 34 root_->Release(); |
|
binji
2012/04/16 22:43:05
set root_ to NULL
noelallen1
2012/04/21 18:09:53
Done.
| |
| 35 void Resize(size_t size) { | 35 } |
| 36 size_t cap = blocks_; | 36 |
| 37 size_t req = (size + s_blksize - 1) / s_blksize; | 37 MountNode *MountMem::AllocatePath(int mode) { |
|
binji
2012/04/16 22:43:05
nit: swap * side
noelallen1
2012/04/21 18:09:53
Done.
| |
| 38 | 38 ino_t ino = AllocateINO(); |
| 39 if ((req > cap) || (req < (cap / 2))) { | 39 |
| 40 char *newdata = (char *) malloc(req * s_blksize); | 40 // If we fail to initialize, let the smart pointer delete it |
|
binji
2012/04/16 22:43:05
Where is the smart pointer?
noelallen1
2012/04/21 18:09:53
Done.
| |
| 41 memcpy(newdata, data_, size); | 41 MountNode *ptr = new MountNodeDir(this, ino, dev_); |
|
binji
2012/04/16 22:43:05
nit: swap * side
noelallen1
2012/04/21 18:09:53
Done.
| |
| 42 free(data_); | 42 if (!ptr->Init(mode, 1002, 1003)) { |
|
binji
2012/04/16 22:43:05
what are 1002, 1003?
noelallen1
2012/04/21 18:09:53
Done.
| |
| 43 stat_.st_size = size; | 43 ptr->Release(); |
| 44 data_ = newdata; | 44 FreeINO(ino); |
| 45 blocks_ = req; | 45 return NULL; |
| 46 } | |
| 47 return ptr; | |
| 48 } | |
| 49 | |
| 50 MountNode *MountMem::AllocateData(int mode) { | |
|
binji
2012/04/16 22:43:05
nit: swap * side
noelallen1
2012/04/21 18:09:53
Done.
| |
| 51 ino_t ino = AllocateINO(); | |
| 52 | |
| 53 // If we fail to initialize, let the smart pointer delete it | |
| 54 MountNode *ptr = new MountNodeMem(this, ino, dev_); | |
|
binji
2012/04/16 22:43:05
nit: swap * side
noelallen1
2012/04/21 18:09:53
Done.
| |
| 55 if (!ptr->Init(mode, 1002, 1003)) { | |
| 56 ptr->Release(); | |
| 57 FreeINO(ino); | |
| 58 return NULL; | |
| 59 } | |
| 60 return ptr; | |
| 61 } | |
| 62 | |
| 63 void MountMem::ReleaseNode(MountNode* node) { | |
| 64 node->Release(); | |
| 65 } | |
| 66 | |
| 67 int MountMem::AllocateINO() { | |
| 68 if (inos_.size() == 0) { | |
| 69 max_ino_ += 8; | |
| 70 for (int a=0; a < 7; a++) { | |
|
binji
2012/04/16 22:43:05
nit: spaces around =
noelallen1
2012/04/21 18:09:53
Done.
| |
| 71 inos_.push_back(max_ino_ - a); | |
| 46 } | 72 } |
| 47 } | 73 } |
| 48 | 74 return max_ino_ - 8; |
|
binji
2012/04/16 22:43:05
How does this work? It doesn't look like anything
noelallen1
2012/04/21 18:09:53
Whoops. Fixed.
| |
| 49 int ino_; | 75 } |
| 50 int ref_count_; | 76 |
| 51 struct stat stat_; | 77 void MountMem::FreeINO(int ino) { |
| 52 void *data_; | 78 inos_.push_back(ino); |
| 53 size_t blocks_; | 79 } |
| 54 pthread_mutex_t lock_; | 80 |
| 55 }; | 81 MountNode *MountMem::FindNode(const Path& path, int type) { |
|
binji
2012/04/16 22:43:05
Can we assert in debug versions that the lock is h
noelallen1
2012/04/21 18:09:53
Unless we specialize the lock, it won't tell us if
| |
| 56 | 82 MountNode *node = root_; |
|
binji
2012/04/16 22:43:05
nit: swap * side
noelallen1
2012/04/21 18:09:53
Done.
| |
| 57 MountMem::MountMem() {} | 83 |
| 58 MountMem::~MountMem() {} | 84 // If there is no root there, we have an error |
| 59 | 85 if (node == NULL) { |
| 60 int MountMem::AddDirEntry(MountMemNode* dir_node, MountMemNode* obj_node, const char *name) { | 86 errno = ENOTDIR; |
| 61 if (strlen(name > 255)) { | 87 return NULL; |
| 88 } | |
| 89 | |
| 90 // We are expecting an "absolute" path from this mount point | |
| 91 if (!path.IsAbsolute()) { | |
| 62 errno = EINVAL; | 92 errno = EINVAL; |
| 63 return -1; | 93 return NULL; |
| 64 } | 94 } |
| 65 | 95 |
| 66 struct dirent* d = (struct dirent *) dir_node->data_; | 96 for (size_t index = 1; node && index < path.Size(); index++) { |
| 67 size_t cnt = dir_node->Size() / sizeof(struct dirent); | 97 // If not a directory, then we have an error so return. |
| 68 size_t off; | 98 if (!node->IsaDir()) { |
| 69 | 99 errno = ENOTDIR; |
| 70 // Find a free location | 100 return NULL; |
| 71 for (off = 0; off < cnt; off++) { | |
| 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 } | 101 } |
| 111 } | 102 |
| 112 | 103 // Find the child node |
| 113 errno = ENOENT; | 104 node = node->FindChild(path.Part(index)); |
| 114 return -1; | 105 } |
| 115 } | 106 |
| 116 | 107 // If we can't find it, fail |
| 117 MountNodeMem* MountMem::AcquireNode(int ino) { | 108 if (NULL == node) return NULL; |
| 118 SimpleAutoLock lock(&lock_); | 109 |
| 119 MountNodeMem *node = inodes_.At(ino); | 110 // If a directory is expected, but it's not a directory, then fail |
| 120 if (node) node->ref_count_++; | 111 if ((type & _S_IFDIR) && !node->IsaDir()) { |
| 112 errno = ENOTDIR; | |
| 113 return NULL; | |
| 114 } | |
| 115 | |
| 116 // If a file is expected, but it's not a file, then fail | |
| 117 if ((type & _S_IFREG) && node->IsaDir()) { | |
| 118 errno = EISDIR; | |
| 119 return NULL; | |
| 120 } | |
| 121 | |
| 122 // We now have a valid directory object, so increment the ref count | |
|
binji
2012/04/16 22:43:05
Where do we increment a refcount?
noelallen1
2012/04/21 18:09:53
Bad comment, fixed.
| |
| 123 // and return it. | |
| 121 return node; | 124 return node; |
| 122 } | 125 } |
| 123 | 126 |
| 124 void MountMem::ReleaseNode(MountMemNode* node) { | 127 MountNode *MountMem::Open(const Path& path, int mode) { |
|
binji
2012/04/16 22:43:05
nit: swap * side
noelallen1
2012/04/21 18:09:53
Done.
| |
| 125 if (node) { | 128 AutoLock lock(&lock_); |
| 126 SimpleAutoLock lock(&lock_); | 129 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 | 130 |
| 184 if (NULL == node) { | 131 if (NULL == node) { |
| 185 errno = BADF; | 132 // Now first find the parent directory to see if we can add it |
| 186 return -1; | 133 MountNode* parent = FindNode(path.Parent(), _S_IFDIR); |
| 187 } | 134 if (NULL == parent) return NULL; |
| 188 | 135 |
| 189 node->stat_.st_mode = mode; | 136 // If the node does not exist and we can't create it, fail |
| 137 if ((mode & O_CREAT) == 0) return NULL; | |
| 138 | |
| 139 // Otherwise, create it with a single refernece | |
| 140 mode = OpenModeToPermission(mode); | |
|
binji
2012/04/16 22:43:05
Is OpenModeToPermission new? I can't find it.
noelallen1
2012/04/21 18:09:53
A public static defined in the parent class.
| |
| 141 node = AllocateData(mode); | |
| 142 if (NULL == node) return NULL; | |
| 143 | |
|
binji
2012/04/16 22:43:05
nit: remove extra blank line
noelallen1
2012/04/21 18:09:53
Done.
| |
| 144 | |
| 145 if (parent->AddChild(path.Filename(), node) == -1) { | |
| 146 // Or if it fails, release it | |
| 147 node->Release(); | |
| 148 return NULL; | |
| 149 } | |
| 150 return node; | |
| 151 } | |
| 152 | |
| 153 // If we were expected to create it exclusively, fail | |
| 154 if (mode & O_EXCL) { | |
| 155 errno = EEXIST; | |
| 156 return NULL; | |
| 157 } | |
| 158 | |
| 159 // Verify we got the requested permisions. | |
| 160 int req_mode = OpenModeToPermission(mode); | |
| 161 int obj_mode = node->GetMode() & OpenModeToPermission(_O_RDWR); | |
| 162 if ((obj_mode & req_mode) != req_mode) { | |
| 163 errno = EACCES; | |
| 164 return NULL; | |
| 165 } | |
| 166 | |
| 167 // We opened it, so ref count it before passing it back. | |
| 168 node->Acquire(); | |
| 169 return node; | |
| 170 } | |
| 171 | |
| 172 int MountMem::Close(MountNode* node) { | |
| 173 AutoLock lock(&lock_); | |
| 174 node->Close(); | |
| 190 ReleaseNode(node); | 175 ReleaseNode(node); |
| 191 return 0; | 176 return 0; |
| 192 } | 177 } |
| 193 | 178 |
| 194 int MountMem::Stat(int ino, struct stat *buf) { | 179 int MountMem::Unlink(const Path& path) { |
| 195 MountMemNode* node = AcquireNode(ino); | 180 AutoLock lock(&lock_); |
| 196 | 181 MountNode* parent = FindNode(path.Parent(), _S_IFDIR); |
| 182 | |
| 183 if (NULL == parent) return -1; | |
| 184 | |
| 185 MountNode* child = parent->FindChild(path.Filename()); | |
| 186 if (NULL == child) { | |
| 187 errno = ENOENT; | |
| 188 return -1; | |
| 189 } | |
| 190 if (child->IsaDir()) { | |
| 191 errno = EISDIR; | |
| 192 return -1; | |
| 193 } | |
| 194 return parent->RemoveChild(path.Filename()); | |
| 195 } | |
| 196 | |
| 197 int MountMem::Mkdir(const Path& path, int mode) { | |
| 198 AutoLock lock(&lock_); | |
| 199 | |
| 200 // We expect a Mount "absolute" path | |
| 201 if (!path.IsAbsolute()) { | |
| 202 errno = ENOENT; | |
| 203 return -1; | |
| 204 } | |
| 205 | |
| 206 // The root of the mount is already created by the mount | |
| 207 if (path.Size() == 1) { | |
| 208 errno = EEXIST; | |
| 209 return -1; | |
| 210 } | |
| 211 | |
| 212 MountNode* parent = FindNode(path.Parent(), _S_IFDIR); | |
| 213 MountNode* node; | |
| 214 | |
| 215 // If we failed to find the parent, the error code is already set. | |
| 216 if (NULL == parent) return -1; | |
| 217 | |
| 218 node = parent->FindChild(path.Filename()); | |
| 219 if (NULL != node) { | |
| 220 errno = EEXIST; | |
| 221 return -1; | |
| 222 } | |
| 223 | |
| 224 // Otherwise, create a new node and attempt to add it | |
| 225 mode = OpenModeToPermission(mode); | |
| 226 node = AllocatePath(_S_IREAD | _S_IWRITE); | |
| 227 if (NULL == node) return -1; | |
| 228 | |
| 229 if (parent->AddChild(path.Filename(), node) == -1) { | |
| 230 node->Release(); | |
| 231 return -1; | |
| 232 } | |
| 233 | |
| 234 node->Release(); | |
|
binji
2012/04/16 22:43:05
Document this release
noelallen1
2012/04/21 18:09:53
Done.
| |
| 235 return 0; | |
| 236 } | |
| 237 | |
| 238 int MountMem::Rmdir(const Path& path) { | |
| 239 AutoLock lock(&lock_); | |
| 240 | |
| 241 // We expect a Mount "absolute" path | |
| 242 if (!path.IsAbsolute()) { | |
| 243 errno = ENOENT; | |
| 244 return -1; | |
| 245 } | |
| 246 | |
| 247 // The root of the mount is already created by the mount | |
| 248 if (path.Size() == 1) { | |
| 249 errno = EEXIST; | |
| 250 return -1; | |
| 251 } | |
| 252 | |
| 253 MountNode* parent = FindNode(path.Parent(), _S_IFDIR); | |
| 254 MountNode* node; | |
| 255 | |
| 256 // If we failed to find the parent, the error code is already set. | |
| 257 if (NULL == parent) return -1; | |
| 258 | |
| 259 // Verify we find a child which is also a directory | |
| 260 node = parent->FindChild(path.Filename()); | |
| 197 if (NULL == node) { | 261 if (NULL == node) { |
| 198 errno = BADF; | 262 errno = ENOENT; |
| 199 return -1; | 263 return -1; |
| 200 } | 264 } |
| 201 | 265 if (!node->IsaDir()) { |
| 202 memcpy(buf, node->stat_, sizeof(struct stat)); | 266 errno = ENOTDIR; |
| 203 ReleaseNode(node); | 267 return -1; |
| 204 return 0; | 268 } |
| 205 } | 269 if (node->ChildCount() > 0) { |
| 206 | 270 errno = ENOTEMPTY; |
| 207 int MountMem::Fsync(int ino) { | 271 return -1; |
| 208 // Acquire the node in case he node | 272 } |
| 209 MountMemNode* node = AcquireNode(ino); | 273 return parent->RemoveChild(path.Filename()); |
| 210 if (node) { | 274 } |
| 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 |