vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / fs / KPath.cpp
blob3debfbf38f7d440926eaa50b4b49481c87151ccf
1 /*
2 * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2008-2017, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
8 /*! A simple class wrapping a path. Has a fixed-sized buffer. */
11 #include <fs/KPath.h>
13 #include <stdlib.h>
14 #include <string.h>
16 #include <team.h>
17 #include <vfs.h>
20 // debugging
21 #define TRACE(x) ;
22 //#define TRACE(x) dprintf x
25 KPath::KPath(size_t bufferSize)
27 fBuffer(NULL),
28 fBufferSize(0),
29 fPathLength(0),
30 fLocked(false),
31 fLazy(false),
32 fFailed(false),
33 fIsNull(false)
35 SetTo(NULL, DEFAULT, bufferSize);
39 KPath::KPath(const char* path, int32 flags, size_t bufferSize)
41 fBuffer(NULL),
42 fBufferSize(0),
43 fPathLength(0),
44 fLocked(false),
45 fLazy(false),
46 fFailed(false),
47 fIsNull(false)
49 SetTo(path, flags, bufferSize);
53 KPath::KPath(const KPath& other)
55 fBuffer(NULL),
56 fBufferSize(0),
57 fPathLength(0),
58 fLocked(false),
59 fLazy(false),
60 fFailed(false),
61 fIsNull(false)
63 *this = other;
67 KPath::~KPath()
69 free(fBuffer);
73 status_t
74 KPath::SetTo(const char* path, int32 flags, size_t bufferSize)
76 if (bufferSize == 0)
77 bufferSize = B_PATH_NAME_LENGTH;
79 // free the previous buffer, if the buffer size differs
80 if (fBuffer != NULL && fBufferSize != bufferSize) {
81 free(fBuffer);
82 fBuffer = NULL;
83 fBufferSize = 0;
86 fPathLength = 0;
87 fLocked = false;
88 fBufferSize = bufferSize;
89 fLazy = (flags & LAZY_ALLOC) != 0;
90 fIsNull = path == NULL;
92 if (path != NULL || !fLazy) {
93 status_t status = _AllocateBuffer();
94 if (status != B_OK)
95 return status;
98 return SetPath(path, flags);
102 void
103 KPath::Adopt(KPath& other)
105 free(fBuffer);
107 fBuffer = other.fBuffer;
108 fBufferSize = other.fBufferSize;
109 fPathLength = other.fPathLength;
110 fLazy = other.fLazy;
111 fFailed = other.fFailed;
112 fIsNull = other.fIsNull;
114 other.fBuffer = NULL;
115 if (!other.fLazy)
116 other.fBufferSize = 0;
117 other.fPathLength = 0;
118 other.fFailed = false;
119 other.fIsNull = other.fLazy;
123 status_t
124 KPath::InitCheck() const
126 if (fBuffer != NULL || (fLazy && !fFailed && fBufferSize != 0))
127 return B_OK;
129 return fFailed ? B_NO_MEMORY : B_NO_INIT;
133 /*! \brief Sets the buffer to \a path.
135 \param flags Understands the following two options:
136 - \c NORMALIZE
137 - \c TRAVERSE_LEAF_LINK
139 status_t
140 KPath::SetPath(const char* path, int32 flags)
142 if (path == NULL && fLazy && fBuffer == NULL) {
143 fIsNull = true;
144 return B_OK;
147 if (fBuffer == NULL) {
148 if (fLazy) {
149 status_t status = _AllocateBuffer();
150 if (status != B_OK)
151 return B_NO_MEMORY;
152 } else
153 return B_NO_INIT;
156 fIsNull = false;
158 if (path != NULL) {
159 if ((flags & NORMALIZE) != 0) {
160 // normalize path
161 status_t status = _Normalize(path,
162 (flags & TRAVERSE_LEAF_LINK) != 0);
163 if (status != B_OK)
164 return status;
165 } else {
166 // don't normalize path
167 size_t length = strlen(path);
168 if (length >= fBufferSize)
169 return B_BUFFER_OVERFLOW;
171 memcpy(fBuffer, path, length + 1);
172 fPathLength = length;
173 _ChopTrailingSlashes();
175 } else {
176 fBuffer[0] = '\0';
177 fPathLength = 0;
178 if (fLazy)
179 fIsNull = true;
181 return B_OK;
185 const char*
186 KPath::Path() const
188 return fIsNull ? NULL : fBuffer;
192 /*! \brief Locks the buffer for external changes.
194 \param force In lazy mode, this will allocate a buffer when set.
195 Otherwise, \c NULL will be returned if set to NULL.
197 char*
198 KPath::LockBuffer(bool force)
200 if (fBuffer == NULL && fLazy) {
201 if (fIsNull && !force)
202 return NULL;
204 _AllocateBuffer();
207 if (fBuffer == NULL || fLocked)
208 return NULL;
210 fLocked = true;
211 fIsNull = false;
213 return fBuffer;
217 void
218 KPath::UnlockBuffer()
220 if (!fLocked) {
221 TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
222 return;
225 fLocked = false;
227 if (fBuffer == NULL)
228 return;
230 fPathLength = strnlen(fBuffer, fBufferSize);
231 if (fPathLength == fBufferSize) {
232 TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
233 fPathLength--;
234 fBuffer[fPathLength] = '\0';
236 _ChopTrailingSlashes();
240 char*
241 KPath::DetachBuffer()
243 char* buffer = fBuffer;
245 if (fBuffer != NULL) {
246 fBuffer = NULL;
247 fPathLength = 0;
248 fLocked = false;
251 return buffer;
255 const char*
256 KPath::Leaf() const
258 if (fBuffer == NULL)
259 return NULL;
261 for (int32 i = fPathLength - 1; i >= 0; i--) {
262 if (fBuffer[i] == '/')
263 return fBuffer + i + 1;
266 return fBuffer;
270 status_t
271 KPath::ReplaceLeaf(const char* newLeaf)
273 const char* leaf = Leaf();
274 if (leaf == NULL)
275 return B_NO_INIT;
277 int32 leafIndex = leaf - fBuffer;
278 // chop off the current leaf (don't replace "/", though)
279 if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
280 fBuffer[leafIndex] = '\0';
281 fPathLength = leafIndex;
282 _ChopTrailingSlashes();
285 // if a leaf was given, append it
286 if (newLeaf != NULL)
287 return Append(newLeaf);
288 return B_OK;
292 bool
293 KPath::RemoveLeaf()
295 // get the leaf -- bail out, if not initialized or only the "/" is left
296 const char* leaf = Leaf();
297 if (leaf == NULL || leaf == fBuffer || leaf[0] == '\0')
298 return false;
300 // chop off the leaf
301 int32 leafIndex = leaf - fBuffer;
302 fBuffer[leafIndex] = '\0';
303 fPathLength = leafIndex;
304 _ChopTrailingSlashes();
306 return true;
310 status_t
311 KPath::Append(const char* component, bool isComponent)
313 // check initialization and parameter
314 if (fBuffer == NULL)
315 return B_NO_INIT;
316 if (component == NULL)
317 return B_BAD_VALUE;
318 if (fPathLength == 0)
319 return SetPath(component);
321 // get component length
322 size_t componentLength = strlen(component);
323 if (componentLength < 1)
324 return B_OK;
326 // if our current path is empty, we just copy the supplied one
327 // compute the result path len
328 bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
329 && component[0] != '/';
330 size_t resultPathLength = fPathLength + componentLength
331 + (insertSlash ? 1 : 0);
332 if (resultPathLength >= fBufferSize)
333 return B_BUFFER_OVERFLOW;
335 // compose the result path
336 if (insertSlash)
337 fBuffer[fPathLength++] = '/';
338 memcpy(fBuffer + fPathLength, component, componentLength + 1);
339 fPathLength = resultPathLength;
340 return B_OK;
344 status_t
345 KPath::Normalize(bool traverseLeafLink)
347 if (fBuffer == NULL)
348 return B_NO_INIT;
349 if (fPathLength == 0)
350 return B_BAD_VALUE;
352 return _Normalize(fBuffer, traverseLeafLink);
356 KPath&
357 KPath::operator=(const KPath& other)
359 SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT,
360 other.fBufferSize);
361 return *this;
365 KPath&
366 KPath::operator=(const char* path)
368 SetPath(path);
369 return *this;
373 bool
374 KPath::operator==(const KPath& other) const
376 if (fBuffer == NULL)
377 return !other.fBuffer;
379 return other.fBuffer != NULL
380 && fPathLength == other.fPathLength
381 && strcmp(fBuffer, other.fBuffer) == 0;
385 bool
386 KPath::operator==(const char* path) const
388 if (fBuffer == NULL)
389 return path == NULL;
391 return path != NULL && strcmp(fBuffer, path) == 0;
395 bool
396 KPath::operator!=(const KPath& other) const
398 return !(*this == other);
402 bool
403 KPath::operator!=(const char* path) const
405 return !(*this == path);
409 status_t
410 KPath::_AllocateBuffer()
412 if (fBuffer == NULL && fBufferSize != 0)
413 fBuffer = (char*)malloc(fBufferSize);
414 if (fBuffer == NULL) {
415 fFailed = true;
416 return B_NO_MEMORY;
419 fBuffer[0] = '\0';
420 fFailed = false;
421 return B_OK;
425 status_t
426 KPath::_Normalize(const char* path, bool traverseLeafLink)
428 status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
429 traverseLeafLink,
430 team_get_kernel_team_id() == team_get_current_team_id());
431 if (error != B_OK) {
432 // vfs_normalize_path() might have screwed up the previous
433 // path -- unset it completely to avoid weird problems.
434 fBuffer[0] = '\0';
435 fPathLength = 0;
436 return error;
439 fPathLength = strlen(fBuffer);
440 return B_OK;
444 void
445 KPath::_ChopTrailingSlashes()
447 if (fBuffer != NULL) {
448 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
449 fBuffer[--fPathLength] = '\0';