vfs: check userland buffers before reading them.
[haiku.git] / src / tools / fs_shell / KPath.cpp
blob4a0ba15fc2abe4e762071b44757d97a0defafa6b
1 /*
2 * Copyright 2004-2007, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
4 */
6 /** A simple class wrapping a path. Has a fixed-sized buffer. */
8 #include "KPath.h"
10 #include <stdlib.h>
12 #include "fssh_string.h"
13 #include "vfs.h"
16 // debugging
17 #define TRACE(x) ;
18 //#define TRACE(x) dprintf x
21 KPath::KPath(fssh_size_t bufferSize)
23 fBuffer(NULL),
24 fBufferSize(0),
25 fPathLength(0),
26 fLocked(false)
28 SetTo(NULL, bufferSize);
32 KPath::KPath(const char* path, bool normalize, fssh_size_t bufferSize)
34 fBuffer(NULL),
35 fBufferSize(0),
36 fPathLength(0),
37 fLocked(false)
39 SetTo(path, normalize, bufferSize);
43 KPath::KPath(const KPath& other)
45 fBuffer(NULL),
46 fBufferSize(0),
47 fPathLength(0),
48 fLocked(false)
50 *this = other;
54 KPath::~KPath()
56 free(fBuffer);
60 fssh_status_t
61 KPath::SetTo(const char* path, bool normalize, fssh_size_t bufferSize)
63 if (bufferSize == 0)
64 bufferSize = FSSH_B_PATH_NAME_LENGTH;
66 // free the previous buffer, if the buffer size differs
67 if (fBuffer && fBufferSize != bufferSize) {
68 free(fBuffer);
69 fBuffer = NULL;
70 fBufferSize = 0;
72 fPathLength = 0;
73 fLocked = false;
75 // allocate buffer
76 if (!fBuffer)
77 fBuffer = (char*)malloc(bufferSize);
78 if (!fBuffer)
79 return FSSH_B_NO_MEMORY;
80 if (fBuffer) {
81 fBufferSize = bufferSize;
82 fBuffer[0] = '\0';
84 return SetPath(path, normalize);
88 fssh_status_t
89 KPath::InitCheck() const
91 return fBuffer ? FSSH_B_OK : FSSH_B_NO_MEMORY;
95 fssh_status_t
96 KPath::SetPath(const char *path, bool normalize)
98 if (!fBuffer)
99 return FSSH_B_NO_INIT;
101 if (path) {
102 if (normalize) {
103 // normalize path
104 fssh_status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
105 true);
106 if (error != FSSH_B_OK) {
107 SetPath(NULL);
108 return error;
110 fPathLength = fssh_strlen(fBuffer);
111 } else {
112 // don't normalize path
113 fssh_size_t length = fssh_strlen(path);
114 if (length >= fBufferSize)
115 return FSSH_B_BUFFER_OVERFLOW;
117 fssh_memcpy(fBuffer, path, length + 1);
118 fPathLength = length;
119 _ChopTrailingSlashes();
121 } else {
122 fBuffer[0] = '\0';
123 fPathLength = 0;
125 return FSSH_B_OK;
129 const char*
130 KPath::Path() const
132 return fBuffer;
136 char *
137 KPath::LockBuffer()
139 if (!fBuffer || fLocked)
140 return NULL;
142 fLocked = true;
143 return fBuffer;
147 void
148 KPath::UnlockBuffer()
150 if (!fLocked) {
151 TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
152 return;
154 fLocked = false;
155 fPathLength = fssh_strnlen(fBuffer, fBufferSize);
156 if (fPathLength == fBufferSize) {
157 TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
158 fPathLength--;
159 fBuffer[fPathLength] = '\0';
161 _ChopTrailingSlashes();
165 const char *
166 KPath::Leaf() const
168 if (!fBuffer)
169 return NULL;
171 // only "/" has trailing slashes -- then we have to return the complete
172 // buffer, as we have to do in case there are no slashes at all
173 if (fPathLength != 1 || fBuffer[0] != '/') {
174 for (int32_t i = fPathLength - 1; i >= 0; i--) {
175 if (fBuffer[i] == '/')
176 return fBuffer + i + 1;
179 return fBuffer;
183 fssh_status_t
184 KPath::ReplaceLeaf(const char *newLeaf)
186 const char *leaf = Leaf();
187 if (!leaf)
188 return FSSH_B_NO_INIT;
190 int32_t leafIndex = leaf - fBuffer;
191 // chop off the current leaf (don't replace "/", though)
192 if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
193 fBuffer[leafIndex] = '\0';
194 fPathLength = leafIndex;
195 _ChopTrailingSlashes();
198 // if a leaf was given, append it
199 if (newLeaf)
200 return Append(newLeaf);
201 return FSSH_B_OK;
205 fssh_status_t
206 KPath::Append(const char *component, bool isComponent)
208 // check initialization and parameter
209 if (!fBuffer)
210 return FSSH_B_NO_INIT;
211 if (!component)
212 return FSSH_B_BAD_VALUE;
213 if (fPathLength == 0)
214 return SetPath(component);
216 // get component length
217 fssh_size_t componentLength = fssh_strlen(component);
218 if (componentLength < 1)
219 return FSSH_B_OK;
221 // if our current path is empty, we just copy the supplied one
222 // compute the result path len
223 bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
224 && component[0] != '/';
225 fssh_size_t resultPathLength = fPathLength + componentLength + (insertSlash ? 1 : 0);
226 if (resultPathLength >= fBufferSize)
227 return FSSH_B_BUFFER_OVERFLOW;
229 // compose the result path
230 if (insertSlash)
231 fBuffer[fPathLength++] = '/';
232 fssh_memcpy(fBuffer + fPathLength, component, componentLength + 1);
233 fPathLength = resultPathLength;
234 return FSSH_B_OK;
238 KPath&
239 KPath::operator=(const KPath& other)
241 SetTo(other.fBuffer, other.fBufferSize);
242 return *this;
246 KPath&
247 KPath::operator=(const char* path)
249 SetTo(path);
250 return *this;
254 bool
255 KPath::operator==(const KPath& other) const
257 if (!fBuffer)
258 return !other.fBuffer;
260 return (other.fBuffer
261 && fPathLength == other.fPathLength
262 && fssh_strcmp(fBuffer, other.fBuffer) == 0);
266 bool
267 KPath::operator==(const char* path) const
269 if (!fBuffer)
270 return (!path);
272 return path && !fssh_strcmp(fBuffer, path);
276 bool
277 KPath::operator!=(const KPath& other) const
279 return !(*this == other);
283 bool
284 KPath::operator!=(const char* path) const
286 return !(*this == path);
290 void
291 KPath::_ChopTrailingSlashes()
293 if (fBuffer) {
294 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
295 fBuffer[--fPathLength] = '\0';