summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/package.cc
diff options
context:
space:
mode:
authorAdam Borowski <kilobyte@angband.pl>2010-10-15 02:12:34 +0200
committerAdam Borowski <kilobyte@angband.pl>2010-10-15 02:12:34 +0200
commite7572d74b30900124d9a896843f42b1c957d20c2 (patch)
tree4498d0d55dd643ae021e93e38d74dafce960bfd9 /crawl-ref/source/package.cc
parent634ab294f302c98be55389a97e94cd7205860310 (diff)
parentacf5187334f2cfb983a1a8de07d1e77f73e4283a (diff)
downloadcrawl-ref-e7572d74b30900124d9a896843f42b1c957d20c2.tar.gz
crawl-ref-e7572d74b30900124d9a896843f42b1c957d20c2.zip
Merge branch 'master' into unicode
Diffstat (limited to 'crawl-ref/source/package.cc')
-rw-r--r--crawl-ref/source/package.cc120
1 files changed, 80 insertions, 40 deletions
diff --git a/crawl-ref/source/package.cc b/crawl-ref/source/package.cc
index ce941d1666..4ec55fc4eb 100644
--- a/crawl-ref/source/package.cc
+++ b/crawl-ref/source/package.cc
@@ -10,8 +10,6 @@ Guarantees:
the exact state it had at the last commit().
Caveats/issues:
-* Before the first commit, the save is "not a valid DCSS save file",
- how should we handle this?
* Benchmarked on random chunks of size 2^random2(X) * frandom(1),
naive reusing of the first slab of free space produces less waste than
best fit. I don't fully understand why, but for now, let's use that.
@@ -93,6 +91,7 @@ package::package(const char* file, bool writeable, bool empty)
{
dprintf("package: initializing file=\"%s\" rw=%d\n", file, writeable);
ASSERT(writeable || !empty);
+ filename = file;
rw = writeable;
if (empty)
@@ -102,7 +101,10 @@ package::package(const char* file, bool writeable, bool empty)
sysfail("can't create save file (%s)", file);
if (!lock_file(fd, true))
+ {
+ close(fd);
sysfail("failed to lock newly created save (%s)", file);
+ }
dirty = true;
file_len = sizeof(file_header);
@@ -113,46 +115,61 @@ package::package(const char* file, bool writeable, bool empty)
if (fd == -1)
sysfail("can't open save file (%s)", file);
- if (!lock_file(fd, writeable))
- fail("Another game is already in progress using this save!");
-
- file_header head;
- ssize_t res = ::read(fd, &head, sizeof(file_header));
- if (res < 0)
- sysfail("error reading the save file (%s)", file);
- if (!res || !(head.magic || head.version || head.padding[0]
- || head.padding[1] || head.padding[2] || head.start))
+ try
{
- fail("The save file (%s) is empty!", file);
+ if (!lock_file(fd, writeable))
+ fail("Another game is already in progress using this save!");
+
+ load();
}
- if (res != sizeof(file_header))
- fail("save file (%s) corrupted -- header truncated", file);
-
- if (htole(head.magic) != PACKAGE_MAGIC)
- fail("save file (%s) corrupted -- not a DCSS save file", file);
- if (head.version != PACKAGE_VERSION)
- fail("save file (%s) uses an unknown format %u", file, head.version);
- off_t len = lseek(fd, 0, SEEK_END);
- if (len == -1)
- sysfail("save file (%s) is not seekable", file);
- file_len = len;
- read_directory(htole(head.start));
-
- if (writeable)
+ catch (std::exception &e)
{
- free_blocks[sizeof(file_header)] = file_len - sizeof(file_header);
+ close(fd);
+ throw;
+ }
+ }
+}
+
+void package::load()
+{
+ file_header head;
+ ssize_t res = ::read(fd, &head, sizeof(file_header));
+ if (res < 0)
+ sysfail("error reading the save file (%s)", filename.c_str());
+ if (!res || !(head.magic || head.version || head.padding[0]
+ || head.padding[1] || head.padding[2] || head.start))
+ {
+ fail("The save file (%s) is empty!", filename.c_str());
+ }
+ if (res != sizeof(file_header))
+ fail("save file (%s) corrupted -- header truncated", filename.c_str());
+
+ if (htole(head.magic) != PACKAGE_MAGIC)
+ fail("save file (%s) corrupted -- not a DCSS save file",
+ filename.c_str());
+ if (head.version != PACKAGE_VERSION)
+ fail("save file (%s) uses an unknown format %u", filename.c_str(),
+ head.version);
+ off_t len = lseek(fd, 0, SEEK_END);
+ if (len == -1)
+ sysfail("save file (%s) is not seekable", filename.c_str());
+ file_len = len;
+ read_directory(htole(head.start));
+
+ if (rw)
+ {
+ free_blocks[sizeof(file_header)] = file_len - sizeof(file_header);
- for(directory_t::iterator ch = directory.begin();
- ch != directory.end(); ch++)
- {
- trace_chunk(ch->second);
- }
+ for(directory_t::iterator ch = directory.begin();
+ ch != directory.end(); ch++)
+ {
+ trace_chunk(ch->second);
+ }
#ifdef COSTLY_ASSERTS
- // any inconsitency in the save is guaranteed to be already found
- // by this time -- this checks only for internal bugs
- fsck();
+ // any inconsitency in the save is guaranteed to be already found
+ // by this time -- this checks only for internal bugs
+ fsck();
#endif
- }
}
}
@@ -173,9 +190,10 @@ package::~package()
}
// all errors here should be cached write errors
- if (close(fd) && !aborted)
- sysfail(rw ? "write error while saving"
- : "can't close the save I've just read???");
+ if (fd != -1)
+ if (close(fd) && !aborted)
+ sysfail(rw ? "write error while saving"
+ : "can't close the save I've just read???");
dprintf("package: closed\n");
}
@@ -194,7 +212,7 @@ void package::commit()
head.magic = htole(PACKAGE_MAGIC);
head.version = PACKAGE_VERSION;
memset(&head.padding, 0, sizeof(head.padding));
- head.start = write_directory();
+ head.start = htole(write_directory());
#ifdef DO_FSYNC
// We need a barrier before updating the link to point at the new directory.
if (fdatasync(fd))
@@ -519,12 +537,28 @@ void package::abort()
aborted = true;
}
+void package::unlink()
+{
+ abort();
+ close(fd);
+ fd = -1;
+ ::unlink_u(filename.c_str());
+}
+
chunk_writer::chunk_writer(package *parent, const std::string _name)
: first_block(0), cur_block(0), block_len(0)
{
ASSERT(parent);
ASSERT(!parent->aborted);
+
+ /*
+ The save format is currently limited to 4 character names for simplicity
+ (we use 3 max). If you want to extend this, please implement marshalling
+ of arbitrary strings in {read,write}_directory().
+ */
+ ASSERT(_name.size() <= 4);
+
dprintf("chunk_writer(%s): starting\n", _name.c_str());
pkg = parent;
pkg->n_users++;
@@ -550,7 +584,13 @@ chunk_writer::~chunk_writer()
ASSERT(pkg->n_users > 0);
pkg->n_users--;
if (pkg->aborted)
+ {
+#ifdef USE_ZLIB
+ // ignore errors, they're not relevant anymore
+ deflateEnd(&zs);
+#endif
return;
+ }
#ifdef USE_ZLIB
zs.avail_in = 0;