Merge lp:~widelands-dev/widelands/filesystem-errors into lp:widelands

Proposed by Arty
Status: Merged
Merged at revision: 8921
Proposed branch: lp:~widelands-dev/widelands/filesystem-errors
Merge into: lp:widelands
Diff against target: 361 lines (+85/-88)
4 files modified
src/io/filesystem/disk_filesystem.cc (+69/-66)
src/io/filesystem/filesystem.cc (+3/-3)
src/io/filesystem/filesystem_exceptions.h (+0/-12)
src/io/filesystem/zip_filesystem.cc (+13/-7)
To merge this branch: bzr merge lp:~widelands-dev/widelands/filesystem-errors
Reviewer Review Type Date Requested Status
GunChleoc Approve
Review via email: mp+358219@code.launchpad.net

Commit message

Improved error checking in filesystem functions
- some filesystem error checking added
- more specific filesystem exceptions thrown

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4186. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/449820523.
Appveyor build 3983. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_filesystem_errors-3983.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4199. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/452677304.
Appveyor build 3995. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_filesystem_errors-3995.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have done a code review with my last commit - please have a look at the diff.

Not tested yet.

review: Approve
Revision history for this message
Arty (artydent) wrote :

regarding code review:

There're plenty more long lines and if-blocks without curly braces in there. I'll fix this tonight.

Do we generally follow a strict rule of curly braces even for short single line blocks?

Revision history for this message
GunChleoc (gunchleoc) wrote :

Our policy is to always use curly braces, as the lack of them can cause bugs. We still have tons of them missing though, so we only fix them if the code is being changed anyway. Too many merge conflicts otherwise.

Revision history for this message
Arty (artydent) wrote :

Oh okay, then I'll leave it.

I am still somewhat fuzzy about some specifics with bazaar, specifically merge conflicts.
Let's say for examply I'd fix all those little things in disk_filesystem.cc, then merge the result into my two child branches (which itself should be fine because those don't make any changes to the file themselve). Then - if I got this right - the only conflicts would arise if someone else is doing other changes to a version of the branches from before the merge (which of course is easily possible during the review) or if someone is changing the file in some other branch. Right?

Revision history for this message
kaputtnik (franku) wrote :

No, then you will have 'diverged' branches. I had those a lot in the past... Mostly i deleted the branch and pulled it again to get a clean copy.

Merge conflicts arise if the logic of the merge command wasn't able to merge branch A into branch B. This can happen if the changes made in branch A are too complicated for the merge logic.

Try in terminal:

bzr help conflicts
bzr help conflict-types
bzr help diverged-branches

Revision history for this message
Arty (artydent) wrote :

Good to know, thanks. So I guess I shouldn't even make any child branches at all before the parent branch is fully revised and scheduled for merging into trunk. Or maybe just not push them online or at least not bother anyone else with them via merge requests; and then later resolve merge conflicts on my own (possibly just copying it over into a new branch) before making everything available. I feel that this kinda destroys what I thought to be a big advantage of this whole branching concept.

Revision history for this message
GunChleoc (gunchleoc) wrote :

You get diverged branches if somebody else pushed to the online version of the branch. You can rescue your changes with:

bzr uncommit
bzr shelve

Then pull the changes:

bzr pull lp:~widelands-dev/widelands/filesystem-errors

Then retrieve the shelved changes:

bzr unshelve

If you getting merge conflicts during the unshelve or then merging trunk, you'll have to manually edit the files to get them in a good state again, then call

bzr resolve

If it doesn't autoresolve:

bzr resolve <filename>

Compile again to make sure that you haven't made any mistakes with the merge, then commit.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Tested.

@bunnybot merge

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/io/filesystem/disk_filesystem.cc'
--- src/io/filesystem/disk_filesystem.cc 2018-04-07 16:59:00 +0000
+++ src/io/filesystem/disk_filesystem.cc 2018-11-12 08:55:49 +0000
@@ -167,15 +167,13 @@
167}167}
168168
169/**169/**
170 * Create a sub filesystem out of this filesystem170 * Make a sub filesystem out of this filesystem
171 */171 */
172FileSystem* RealFSImpl::make_sub_file_system(const std::string& path) {172FileSystem* RealFSImpl::make_sub_file_system(const std::string& path) {
173 FileSystemPath fspath(canonicalize_name(path));173 FileSystemPath fspath(canonicalize_name(path));
174174
175 if (!fspath.exists_) {175 if (!fspath.exists_) {
176 throw wexception("RealFSImpl: unable to create sub filesystem, path does not exist for '%s'"176 throw FileNotFoundError("RealFSImpl::make_sub_file_system", fspath);
177 " in directory '%s'",
178 fspath.c_str(), directory_.c_str());
179 }177 }
180178
181 if (fspath.is_directory_)179 if (fspath.is_directory_)
@@ -190,10 +188,7 @@
190FileSystem* RealFSImpl::create_sub_file_system(const std::string& path, Type const fs) {188FileSystem* RealFSImpl::create_sub_file_system(const std::string& path, Type const fs) {
191 FileSystemPath fspath(canonicalize_name(path));189 FileSystemPath fspath(canonicalize_name(path));
192 if (fspath.exists_)190 if (fspath.exists_)
193 throw wexception(191 throw FileError("RealFSImpl::create_sub_file_system", fspath, "path already exists, cannot create new filesystem from it");
194 "path '%s'' already exists in directory '%s', can not create a filesystem from it",
195 path.c_str(), directory_.c_str());
196
197 if (fs == FileSystem::DIR) {192 if (fs == FileSystem::DIR) {
198 ensure_directory_exists(path);193 ensure_directory_exists(path);
199 return new RealFSImpl(fspath);194 return new RealFSImpl(fspath);
@@ -221,20 +216,22 @@
221void RealFSImpl::unlink_file(const std::string& file) {216void RealFSImpl::unlink_file(const std::string& file) {
222 FileSystemPath fspath(canonicalize_name(file));217 FileSystemPath fspath(canonicalize_name(file));
223 if (!fspath.exists_) {218 if (!fspath.exists_) {
224 throw wexception(219 throw FileNotFoundError("RealFSImpl::unlink_file", fspath);
225 "RealFSImpl: unable to unlink file, path does not exist for '%s' in directory '%s'",
226 fspath.c_str(), directory_.c_str());
227 }220 }
228 if (fspath.is_directory_) {221 if (fspath.is_directory_) {
229 throw wexception(222 throw FileTypeError("RealFSImpl::unlink_file", fspath, "path is a directory");
230 "RealFSImpl: unable to unlink file, path '%s' in directory '%s' is a directory",
231 fspath.c_str(), directory_.c_str());
232 }223 }
233
234#ifndef _WIN32224#ifndef _WIN32
235 unlink(fspath.c_str());225 if (unlink(fspath.c_str()) == -1)
226 throw FileError("RealFSImpl::unlink_file", fspath, strerror(errno));
236#else227#else
237 DeleteFile(fspath.c_str());228 // Note: We could possibly replace this with _unlink()
229 // which would then also work with errno instead of GetLastError(),
230 // but I am not sure if this is available (and works properly)
231 // on all windows platforms or only with Visual Studio.
232 if (!DeleteFile(fspath.c_str()))
233 throw FileError("RealFSImpl::unlink_file", fspath, std::string("file error (Windows error code ")+std::to_string(GetLastError())+")");
234 // TODO(Arty): generate proper system message from GetLastError() via FormatMessage
238#endif235#endif
239}236}
240237
@@ -244,14 +241,10 @@
244void RealFSImpl::unlink_directory(const std::string& file) {241void RealFSImpl::unlink_directory(const std::string& file) {
245 FileSystemPath fspath(canonicalize_name(file));242 FileSystemPath fspath(canonicalize_name(file));
246 if (!fspath.exists_) {243 if (!fspath.exists_) {
247 throw wexception("RealFSImpl: unable to unlink directory, path does not exist for '%s'"244 throw FileNotFoundError("RealFSImpl::unlink_directory", fspath);
248 " in directory '%s'",
249 fspath.c_str(), directory_.c_str());
250 }245 }
251 if (!fspath.is_directory_) {246 if (!fspath.is_directory_) {
252 throw wexception("RealFSImpl: unable to unlink directory, path '%s' in directory '%s'"247 throw FileTypeError("RealFSImpl::unlink_directory", fspath, "path is not a directory");
253 " is not a directory",
254 fspath.c_str(), directory_.c_str());
255 }248 }
256249
257 FilenameSet files = list_directory(file);250 FilenameSet files = list_directory(file);
@@ -268,14 +261,18 @@
268 unlink_file(*pname);261 unlink_file(*pname);
269 }262 }
270263
271// NOTE: this might fail if this directory contains CVS dir,264// NOTE: this might fail if this directory contains CVS dir
272// so no error checking here
273#ifndef _WIN32265#ifndef _WIN32
274 rmdir(fspath.c_str());266 if (rmdir(fspath.c_str()) == -1)
267 throw FileError("RealFSImpl::unlink_directory", fspath, strerror(errno));
275#else268#else
269 // Note: We could possibly replace this with _rmdir()
270 // which would then also work with errno instead of GetLastError(),
271 // but I am not sure if this is available (and works properly)
272 // on all windows platforms or only with Visual Studio.
276 if (!RemoveDirectory(fspath.c_str()))273 if (!RemoveDirectory(fspath.c_str()))
277 throw wexception(274 throw FileError("RealFSImpl::unlink_directory", fspath, std::string("file error (Windows error code ")+std::to_string(GetLastError())+")");
278 "'%s' could not be deleted in directory '%s'.", fspath.c_str(), directory_.c_str());275 // TODO(Arty): generate proper system message from GetLastError() via FormatMessage
279#endif276#endif
280}277}
281278
@@ -292,25 +289,23 @@
292 boost::replace_all(clean_dirname, "\\", "/");289 boost::replace_all(clean_dirname, "\\", "/");
293#endif290#endif
294291
295 try {292 std::string::size_type it = 0;
296 std::string::size_type it = 0;293 while (it < clean_dirname.size()) {
297 while (it < clean_dirname.size()) {294 it = clean_dirname.find('/', it);
298 it = clean_dirname.find('/', it);295
299296 FileSystemPath fspath(canonicalize_name(clean_dirname.substr(0, it)));
300 FileSystemPath fspath(canonicalize_name(clean_dirname.substr(0, it)));297 if (fspath.exists_ && !fspath.is_directory_) {
301 if (fspath.exists_ && !fspath.is_directory_)298 throw FileTypeError("RealFSImpl::ensure_directory_exists",
302 throw wexception("'%s' in directory '%s' exists and is not a directory",299 fspath, "path is not a directory");
303 clean_dirname.substr(0, it).c_str(), directory_.c_str());300 }
304 if (!fspath.exists_)301 if (!fspath.exists_) {
305 make_directory(clean_dirname.substr(0, it));302 make_directory(clean_dirname.substr(0, it));
306303 }
307 if (it == std::string::npos)304
308 break;305 if (it == std::string::npos) {
309 ++it;306 break;
310 }307 }
311 } catch (const std::exception& e) {308 ++it;
312 throw wexception("RealFSImpl::ensure_directory_exists(%s) in directory '%s': %s",
313 clean_dirname.c_str(), directory_.c_str(), e.what());
314 }309 }
315}310}
316311
@@ -318,33 +313,37 @@
318 * Create this directory, throw an error if it already exists or313 * Create this directory, throw an error if it already exists or
319 * if a file is in the way or if the creation fails.314 * if a file is in the way or if the creation fails.
320 *315 *
321 * Pleas note, this function does not honor parents,316 * Please note, this function does not honor parents,
322 * make_directory("onedir/otherdir/onemoredir") will fail317 * make_directory("onedir/otherdir/onemoredir") will fail
323 * if either onedir or otherdir is missing318 * if either onedir or otherdir is missing
324 */319 */
325void RealFSImpl::make_directory(const std::string& dirname) {320void RealFSImpl::make_directory(const std::string& dirname) {
326 FileSystemPath fspath(canonicalize_name(dirname));321 FileSystemPath fspath(canonicalize_name(dirname));
327 if (fspath.exists_)322 if (fspath.exists_)
328 throw wexception("a file/directory with the name '%s' already exists in directory '%s'",323 throw FileError("RealFSImpl::make_directory", fspath, "a file or directory with that name already exists");
329 dirname.c_str(), directory_.c_str());
330324
331 if325#ifndef _WIN32
332#ifdef _WIN32326 if (mkdir(fspath.c_str(), 0x1FF) == -1)
333 (!CreateDirectory(fspath.c_str(), NULL))327 throw FileError("RealFSImpl::make_directory", fspath, strerror(errno));
334#else328#else
335 (mkdir(fspath.c_str(), 0x1FF) == -1)329 // Note: We could possibly replace this with _mkdir()
330 // which would then also work with errno instead of GetLastError(),
331 // but I am not sure if this is available (and works properly)
332 // on all windows platforms or only with Visual Studio.
333 if (!CreateDirectory(fspath.c_str(), NULL))
334 throw FileError("RealFSImpl::make_directory", fspath, std::string("file error (Windows error code ")+std::to_string(GetLastError())+")");
335 // TODO(Arty): generate proper system message from GetLastError() via FormatMessage
336#endif336#endif
337 throw DirectoryCannotCreateError("RealFSImpl::make_directory", dirname, strerror(errno));
338}337}
339338
340/**339/**
341 * Read the given file into alloced memory; called by FileRead::open.340 * Read the given file into allocated memory; called by FileRead::open.
342 * Throws an exception if the file couldn't be opened.341 * Throws an exception if the file couldn't be opened.
343 */342 */
344void* RealFSImpl::load(const std::string& fname, size_t& length) {343void* RealFSImpl::load(const std::string& fname, size_t& length) {
345 const std::string fullname = canonicalize_name(fname);344 const std::string fullname = canonicalize_name(fname);
346 if (is_directory(fullname)) {345 if (is_directory(fullname)) {
347 throw FileError("RealFSImpl::load", fullname);346 throw FileTypeError("RealFSImpl::load", fullname, "path is a directory");
348 }347 }
349348
350 FILE* file = nullptr;349 FILE* file = nullptr;
@@ -353,7 +352,7 @@
353 try {352 try {
354 file = fopen(fullname.c_str(), "rb");353 file = fopen(fullname.c_str(), "rb");
355 if (!file)354 if (!file)
356 throw FileError("RealFSImpl::load", fullname);355 throw FileError("RealFSImpl::load", fullname, "could not open file for reading");
357356
358 // determine the size of the file (rather quirky, but it doesn't require357 // determine the size of the file (rather quirky, but it doesn't require
359 // potentially unportable functions)358 // potentially unportable functions)
@@ -371,6 +370,9 @@
371370
372 // allocate a buffer and read the entire file into it371 // allocate a buffer and read the entire file into it
373 data = malloc(size + 1); // TODO(unknown): memory leak!372 data = malloc(size + 1); // TODO(unknown): memory leak!
373 if (!data)
374 throw wexception("RealFSImpl::load: memory allocation failed for reading file %s (%s) with size %" PRIuS "",
375 fname.c_str(), fullname.c_str(), size);
374 int result = fread(data, size, 1, file);376 int result = fread(data, size, 1, file);
375 if (size && (result != 1)) {377 if (size && (result != 1)) {
376 throw wexception("RealFSImpl::load: read failed for %s (%s) with size %" PRIuS "",378 throw wexception("RealFSImpl::load: read failed for %s (%s) with size %" PRIuS "",
@@ -383,10 +385,12 @@
383385
384 length = size;386 length = size;
385 } catch (...) {387 } catch (...) {
386 if (file) {388 if (file) {
387 fclose(file);389 fclose(file);
388 }390 }
389 free(data);391 if (data) {
392 free(data);
393 }
390 throw;394 throw;
391 }395 }
392396
@@ -410,7 +414,7 @@
410414
411 FILE* const f = fopen(fullname.c_str(), append ? "a" : "wb");415 FILE* const f = fopen(fullname.c_str(), append ? "a" : "wb");
412 if (!f)416 if (!f)
413 throw wexception("could not open %s (%s) for writing", fname.c_str(), fullname.c_str());417 throw FileError("RealFSImpl::write", fullname, "could not open file for writing");
414418
415 size_t const c = fwrite(data, length, 1, f);419 size_t const c = fwrite(data, length, 1, f);
416 fclose(f);420 fclose(f);
@@ -424,8 +428,7 @@
424 const std::string fullname1 = canonicalize_name(old_name);428 const std::string fullname1 = canonicalize_name(old_name);
425 const std::string fullname2 = canonicalize_name(new_name);429 const std::string fullname2 = canonicalize_name(new_name);
426 if (rename(fullname1.c_str(), fullname2.c_str()) != 0)430 if (rename(fullname1.c_str(), fullname2.c_str()) != 0)
427 throw wexception("DiskFileSystem: unable to rename %s to %s: %s", fullname1.c_str(),431 throw FileError("RealFSImpl::fs_rename", fullname1, std::string("unable to rename file to ") + fullname2 + ", " + strerror(errno));
428 fullname2.c_str(), strerror(errno));
429}432}
430433
431/*****************************************************************************434/*****************************************************************************
@@ -439,7 +442,7 @@
439struct RealFSStreamRead : public StreamRead {442struct RealFSStreamRead : public StreamRead {
440 explicit RealFSStreamRead(const std::string& fname) : file_(fopen(fname.c_str(), "rb")) {443 explicit RealFSStreamRead(const std::string& fname) : file_(fopen(fname.c_str(), "rb")) {
441 if (!file_)444 if (!file_)
442 throw wexception("could not open %s for reading", fname.c_str());445 throw FileError("RealFSStreamRead::RealFSStreamRead", fname, "could not open file for reading");
443 }446 }
444447
445 ~RealFSStreamRead() override {448 ~RealFSStreamRead() override {
@@ -477,7 +480,7 @@
477 explicit RealFSStreamWrite(const std::string& fname) : filename_(fname) {480 explicit RealFSStreamWrite(const std::string& fname) : filename_(fname) {
478 file_ = fopen(fname.c_str(), "wb");481 file_ = fopen(fname.c_str(), "wb");
479 if (!file_)482 if (!file_)
480 throw wexception("could not open %s for writing", fname.c_str());483 throw FileError("RealFSStreamWrite::RealFSStreamWrite", fname, "could not open file for writing");
481 }484 }
482485
483 ~RealFSStreamWrite() override {486 ~RealFSStreamWrite() override {
484487
=== modified file 'src/io/filesystem/filesystem.cc'
--- src/io/filesystem/filesystem.cc 2018-07-12 04:41:20 +0000
+++ src/io/filesystem/filesystem.cc 2018-11-12 08:55:49 +0000
@@ -306,7 +306,7 @@
306 std::list<std::string>::iterator i;306 std::list<std::string>::iterator i;
307307
308#ifdef _WIN32308#ifdef _WIN32
309 // remove all slashes with backslashes so following can work.309 // replace all slashes with backslashes so following can work.
310 for (uint32_t j = 0; j < path.size(); ++j) {310 for (uint32_t j = 0; j < path.size(); ++j) {
311 if (path[j] == '/')311 if (path[j] == '/')
312 path[j] = '\\';312 path[j] = '\\';
@@ -463,8 +463,8 @@
463 fs.ensure_directory_exists(".widelands");463 fs.ensure_directory_exists(".widelands");
464 fs.fs_unlink(".widelands");464 fs.fs_unlink(".widelands");
465 return true;465 return true;
466 } catch (...) {466 } catch (const FileError& e) {
467 log("Directory %s is not writeable - next try\n", path);467 log("Directory %s is not writeable - next try: %s\n", path, e.what());
468 }468 }
469469
470 return false;470 return false;
471471
=== modified file 'src/io/filesystem/filesystem_exceptions.h'
--- src/io/filesystem/filesystem_exceptions.h 2018-04-07 16:59:00 +0000
+++ src/io/filesystem/filesystem_exceptions.h 2018-11-12 08:55:49 +0000
@@ -76,16 +76,4 @@
76 }76 }
77};77};
7878
79/**
80 * The directory cannot be created
81 */
82class DirectoryCannotCreateError : public FileError {
83public:
84 explicit DirectoryCannotCreateError(const std::string& thrower,
85 const std::string& dirname,
86 const std::string& message = "cannot create directory")
87
88 : FileError(thrower, dirname, message) {
89 }
90};
91#endif // end of include guard: WL_IO_FILESYSTEM_FILESYSTEM_EXCEPTIONS_H79#endif // end of include guard: WL_IO_FILESYSTEM_FILESYSTEM_EXCEPTIONS_H
9280
=== modified file 'src/io/filesystem/zip_filesystem.cc'
--- src/io/filesystem/zip_filesystem.cc 2018-10-21 22:21:34 +0000
+++ src/io/filesystem/zip_filesystem.cc 2018-11-12 08:55:49 +0000
@@ -268,12 +268,14 @@
268 if (!file_exists(path)) {268 if (!file_exists(path)) {
269 throw wexception(269 throw wexception(
270 "ZipFilesystem::make_sub_file_system: The path '%s' does not exist in zip file '%s'.",270 "ZipFilesystem::make_sub_file_system: The path '%s' does not exist in zip file '%s'.",
271 path.c_str(), zip_file_->path().c_str());271 (basedir_in_zip_file_.empty() ? path : basedir_in_zip_file_ + "/" + path).c_str(),
272 zip_file_->path().c_str());
272 }273 }
273 if (!is_directory(path)) {274 if (!is_directory(path)) {
274 throw wexception("ZipFilesystem::make_sub_file_system: "275 throw wexception(
275 "The path '%s' needs to be a directory in zip file '%s'.",276 "ZipFilesystem::make_sub_file_system: The path '%s' needs to be a directory in zip file '%s'.",
276 path.c_str(), zip_file_->path().c_str());277 (basedir_in_zip_file_.empty() ? path : basedir_in_zip_file_ + "/" + path).c_str(),
278 zip_file_->path().c_str());
277 }279 }
278280
279 std::string localpath = path;281 std::string localpath = path;
@@ -291,7 +293,10 @@
291// see Filesystem::create293// see Filesystem::create
292FileSystem* ZipFilesystem::create_sub_file_system(const std::string& path, Type const type) {294FileSystem* ZipFilesystem::create_sub_file_system(const std::string& path, Type const type) {
293 if (file_exists(path)) {295 if (file_exists(path)) {
294 throw wexception("ZipFilesystem::create_sub_file_system: Sub file system already exists.");296 throw wexception(
297 "ZipFilesystem::create_sub_file_system: Path '%s' already exists in zip file %s.",
298 (basedir_in_zip_file_.empty() ? path : basedir_in_zip_file_ + "/" + path).c_str(),
299 zip_file_->path().c_str());
295 }300 }
296301
297 if (type != FileSystem::DIR)302 if (type != FileSystem::DIR)
@@ -438,8 +443,9 @@
438 "ZipFilesystem::write", complete_filename,443 "ZipFilesystem::write", complete_filename,
439 (boost::format("in path '%s'', Error") % zip_file_->path() % strerror(errno)).str());444 (boost::format("in path '%s'', Error") % zip_file_->path() % strerror(errno)).str());
440 default:445 default:
441 throw FileError("ZipFilesystem::write", complete_filename,446 throw FileError(
442 (boost::format("in path '%s'") % zip_file_->path()).str());447 "ZipFilesystem::write", complete_filename,
448 (boost::format("in path '%s'") % zip_file_->path()).str());
443 }449 }
444450
445 zipCloseFileInZip(zip_file_->write_handle());451 zipCloseFileInZip(zip_file_->write_handle());

Subscribers

People subscribed via source and target branches

to status/vote changes: