[Hivex][PATCH v2] Report last-modified time of hive root and nodes
The infrastructure for modified-time reporting has been essentially unused. These changes report the registry time by treating the time fields as Windows filetime fields stored in little-Endian (which means they can be treated as a single 64-bit little-Endian integer). This patch adds the node_mtime function to the visitor API. Signed-off-by: Alex Nelson <ajnelson@cs.ucsc.edu> --- generator/generator.ml | 3 ++ lib/hivex.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++-- xml/hivexml.c | 18 +++++++++- 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/generator/generator.ml b/generator/generator.ml index 31478cd..36615f7 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -772,6 +772,7 @@ struct hivex_visitor { int (*value_none) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); int (*value_other) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); int (*value_any) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); + int (*node_mtime) (hive_h *h, void *writer_v, hive_node_h node, const char *last_modified); }; #define HIVEX_VISIT_SKIP_BAD 1 @@ -1134,6 +1135,8 @@ all, set the function pointer to NULL. */ int (*value_any) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); + int (*node_mtime) (hive_h *h, void *writer_v, hive_node_h node, + const char *last_modified) }; =over 4 diff --git a/lib/hivex.c b/lib/hivex.c index fedbb6c..4f3dcb1 100644 --- a/lib/hivex.c +++ b/lib/hivex.c @@ -33,6 +33,7 @@ #include <sys/mman.h> #include <sys/stat.h> #include <assert.h> +#include <time.h> #include "c-ctype.h" #include "full-read.h" @@ -93,6 +94,7 @@ struct hive_h { /* Fields from the header, extracted from little-endianness hell. */ size_t rootoffs; /* Root key offset (always an nk-block). */ size_t endpages; /* Offset of end of pages. */ + char *last_modified; /* mtime of base block. */ /* For writing. */ size_t endblocks; /* Offset to next block allocation (0 @@ -104,7 +106,7 @@ struct ntreg_header { char magic[4]; /* "regf" */ uint32_t sequence1; uint32_t sequence2; - char last_modified[8]; + uint64_t last_modified; uint32_t major_ver; /* 1 */ uint32_t minor_ver; /* 3 */ uint32_t unknown5; /* 0 */ @@ -173,7 +175,7 @@ struct ntreg_nk_record { int32_t seg_len; /* length (always -ve because used) */ char id[2]; /* "nk" */ uint16_t flags; - char timestamp[8]; + uint64_t timestamp; uint32_t unknown1; uint32_t parent; /* offset of owner/parent */ uint32_t nr_subkeys; /* number of subkeys */ @@ -265,7 +267,34 @@ header_checksum (const hive_h *h) return sum; } +#define WINDOWS_TICK 10000000LL +#define SEC_TO_UNIX_EPOCH 11644473600LL +/** + * Convert Windows filetime to ISO 8601 format. + * Source for filetime->time_t conversion: http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-secon... + * Source for time_t->char* conversion: Fiwalk version 0.6.14's fiwalk.cpp. + * @param windows_ticks Expected to not have any remaining Endian issues. + */ +int +filetime_to_8601 (char *buf, int bufsize, uint64_t windows_ticks) +{ + if (buf == NULL) { + fprintf (stderr, "filetime_to_8601: Received null output buffer, unable to proceed.\n"); + return -1; + } + uint64_t nanos = windows_ticks % WINDOWS_TICK; + time_t tt = (windows_ticks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH); + struct tm time_tm; + if (gmtime_r (&tt, &time_tm) == NULL) { + fprintf (stderr, "filetime_to_8601: Error running gmtime_r on timestamp (decimal hundreds of ns: %" PRIu64 ").\n", windows_ticks); + return -1; + } + strftime(buf, bufsize, "%FT%TZ", &time_tm); + return 0; +} + #define HIVEX_OPEN_MSGLVL_MASK (HIVEX_OPEN_VERBOSE|HIVEX_OPEN_DEBUG) +#define TIMESTAMP_BUF_LEN 32 hive_h * hivex_open (const char *filename, int flags) @@ -359,6 +388,15 @@ hivex_open (const char *filename, int flags) goto error; } + /* Last-modified time. */ + h->last_modified = (char *) calloc(1 + TIMESTAMP_BUF_LEN, sizeof(char)); + int ft_rc = filetime_to_8601(h->last_modified, TIMESTAMP_BUF_LEN, le64toh ((uint64_t) h->hdr->last_modified)); + if (ft_rc) { + fprintf (stderr, "hivex: failed to parse time value\n"); + free(h->last_modified); + h->last_modified = NULL; + } + if (h->msglvl >= 2) { char *name = windows_utf16_to_utf8 (h->hdr->name, 64); @@ -367,6 +405,8 @@ hivex_open (const char *filename, int flags) " file version %" PRIu32 ".%" PRIu32 "\n" " sequence nos %" PRIu32 " %" PRIu32 "\n" " (sequences nos should match if hive was synched at shutdown)\n" + " last modified %s\n" + " (decimal, 100 ns) %" PRIu64 "\n" " original file name %s\n" " (only 32 chars are stored, name is probably truncated)\n" " root offset 0x%x + 0x1000\n" @@ -374,6 +414,8 @@ hivex_open (const char *filename, int flags) " checksum 0x%x (calculated 0x%x)\n", major_ver, le32toh (h->hdr->minor_ver), le32toh (h->hdr->sequence1), le32toh (h->hdr->sequence2), + h->last_modified, + le64toh (h->hdr->last_modified), name ? name : "(conversion failed)", le32toh (h->hdr->offset), le32toh (h->hdr->blocks), h->size, @@ -541,6 +583,10 @@ hivex_close (hive_h *h) if (h->msglvl >= 1) fprintf (stderr, "hivex_close\n"); + if (h->last_modified) { + free (h->last_modified); + h->last_modified = NULL; + } free (h->bitmap); if (!h->writable) munmap (h->addr, h->size); @@ -608,6 +654,33 @@ hivex_node_name (hive_h *h, hive_node_h node) return ret; } +/* Caller responsible for freeing returned char* if non-null. */ +char * +hivex_node_mtime (hive_h *h, hive_node_h node) +{ + int ft_rc; + if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { + errno = EINVAL; + return NULL; + } + + struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); + + char *ret = calloc (32 + 1, sizeof(char)); + if (ret == NULL) + return ret; + ft_rc = filetime_to_8601 (ret, 32, le64toh (nk->timestamp)); + if (h->msglvl >= 2) { + fprintf(stderr, "hivex_node_mtime: nk->timestamp: %" PRIu64 "\n", nk->timestamp); + fprintf(stderr, "hivex_node_mtime: ret: %s\n", ret); + } + if (ft_rc) { + free(ret); + ret = NULL; + } + return ret; +} + #if 0 /* I think the documentation for the sk and classname fields in the nk * record is wrong, or else the offset field is in the wrong place. @@ -1560,6 +1633,7 @@ hivex__visit_node (hive_h *h, hive_node_h node, { int skip_bad = flags & HIVEX_VISIT_SKIP_BAD; char *name = NULL; + char *last_modified = NULL; hive_value_h *values = NULL; hive_node_h *children = NULL; char *key = NULL; @@ -1584,10 +1658,24 @@ hivex__visit_node (hive_h *h, hive_node_h node, BITMAP_CLR (unvisited, node); name = hivex_node_name (h, node); + last_modified = hivex_node_mtime (h, node); + if (h->msglvl >= 2) + fprintf(stderr, "hivex__visit_node: last_modified: %s\n", last_modified ? last_modified : "NULL" ); if (!name) return skip_bad ? 0 : -1; + + /* Report extra for hive's root node. */ + if (node == hivex_root (h)) { + //Produce mtime for hive, not present node + if (vtor->node_mtime && vtor->node_mtime (h, opaque, node, h->last_modified) == -1) + goto error; + } + if (vtor->node_start && vtor->node_start (h, opaque, node, name) == -1) goto error; + if (vtor->node_mtime && vtor->node_mtime (h, opaque, node, last_modified) == -1) + goto error; + values = hivex_node_values (h, node); if (!values) { ret = skip_bad ? 0 : -1; @@ -1764,6 +1852,7 @@ hivex__visit_node (hive_h *h, hive_node_h node, error: free (name); + free (last_modified); free (values); free (children); free (key); @@ -2264,7 +2353,7 @@ hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name) nk->sk = htole32 (parent_sk_offset - 0x1000); /* Inherit parent timestamp. */ - memcpy (nk->timestamp, parent_nk->timestamp, sizeof (parent_nk->timestamp)); + nk->timestamp = parent_nk->timestamp; /* What I found out the hard way (not documented anywhere): the * subkeys in lh-records must be kept sorted. If you just add a diff --git a/xml/hivexml.c b/xml/hivexml.c index 90cb22b..25e8cd9 100644 --- a/xml/hivexml.c +++ b/xml/hivexml.c @@ -50,6 +50,7 @@ static int value_qword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t static int value_binary (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); static int value_none (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); static int value_other (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); +static int node_mtime (hive_h *h, void *writer_v, hive_node_h node, const char *last_modified); static struct hivex_visitor visitor = { .node_start = node_start, @@ -61,7 +62,8 @@ static struct hivex_visitor visitor = { .value_qword = value_qword, .value_binary = value_binary, .value_none = value_none, - .value_other = value_other + .value_other = value_other, + .node_mtime = node_mtime }; #define XML_CHECK(proc, args) \ @@ -142,6 +144,20 @@ main (int argc, char *argv[]) } static int +node_mtime (hive_h *h, void *writer_v, hive_node_h node, const char *last_modified) +{ + if (!last_modified) { + fprintf(stderr, "node_mtime: last_modified came across NULL.\n"); + return -1; + } + xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; + XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime")); + XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST last_modified)); + XML_CHECK (xmlTextWriterEndElement, (writer)); + return 0; +} + +static int node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name) { xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; -- 1.7.6
On Wed, Aug 10, 2011 at 12:07:23PM -0700, Alex Nelson wrote:
The infrastructure for modified-time reporting has been essentially unused. These changes report the registry time by treating the time fields as Windows filetime fields stored in little-Endian (which means they can be treated as a single 64-bit little-Endian integer).
This patch adds the node_mtime function to the visitor API.
Nearly there. See my comments below.
@@ -93,6 +94,7 @@ struct hive_h { /* Fields from the header, extracted from little-endianness hell. */ size_t rootoffs; /* Root key offset (always an nk-block). */ size_t endpages; /* Offset of end of pages. */ + char *last_modified; /* mtime of base block. */
This field seems to be unused except in debugging messages? Unless you're going to use it, I'd just omit this field and all the code that generates and prints last_modified.
+#define WINDOWS_TICK 10000000LL +#define SEC_TO_UNIX_EPOCH 11644473600LL +/** + * Convert Windows filetime to ISO 8601 format. + * Source for filetime->time_t conversion: http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-secon... + * Source for time_t->char* conversion: Fiwalk version 0.6.14's fiwalk.cpp. + * @param windows_ticks Expected to not have any remaining Endian issues. + */ +int +filetime_to_8601 (char *buf, int bufsize, uint64_t windows_ticks)
This function is an oddity. You're taking a useful thing (64 bit Windows filetime) and converting it to a string. So callers are going to need to parse this string if they want to do anything useful with it. I think it's best just to return the 64 bit Windows filetime (but with the correct endianness) and let callers deal with it. Passing back strings from a C API just seems wrong.
+/* Caller responsible for freeing returned char* if non-null. */ +char * +hivex_node_mtime (hive_h *h, hive_node_h node)
Add this function to the API (in generator/generator.ml, 'functions'). This will ensure that Perl, OCaml etc bindings + documentation are generated automatically for the function. Don't make it return a string. Have it return the original 64 bit Windows filetime instead (ie. RInt64) with the correct endianness, and let callers deal with it. The rest seems fine to me, except that you'll need to push the filetime_to_8601 function into hivexml.c since it will now be getting a 64 bit filetime. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
On Aug 10, 2011, at 12:55 , Richard W.M. Jones wrote:
On Wed, Aug 10, 2011 at 12:07:23PM -0700, Alex Nelson wrote:
The infrastructure for modified-time reporting has been essentially unused. These changes report the registry time by treating the time fields as Windows filetime fields stored in little-Endian (which means they can be treated as a single 64-bit little-Endian integer).
This patch adds the node_mtime function to the visitor API.
Nearly there. See my comments below.
@@ -93,6 +94,7 @@ struct hive_h { /* Fields from the header, extracted from little-endianness hell. */ size_t rootoffs; /* Root key offset (always an nk-block). */ size_t endpages; /* Offset of end of pages. */ + char *last_modified; /* mtime of base block. */
This field seems to be unused except in debugging messages? Unless you're going to use it, I'd just omit this field and all the code that generates and prints last_modified. There is a spot where it is printed: in lib/hivex.c:hivex__visit_node, "Report extra for hive's root node".
+#define WINDOWS_TICK 10000000LL +#define SEC_TO_UNIX_EPOCH 11644473600LL +/** + * Convert Windows filetime to ISO 8601 format. + * Source for filetime->time_t conversion: http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-secon... + * Source for time_t->char* conversion: Fiwalk version 0.6.14's fiwalk.cpp. + * @param windows_ticks Expected to not have any remaining Endian issues. + */ +int +filetime_to_8601 (char *buf, int bufsize, uint64_t windows_ticks)
This function is an oddity. You're taking a useful thing (64 bit Windows filetime) and converting it to a string. So callers are going to need to parse this string if they want to do anything useful with it. I think it's best just to return the 64 bit Windows filetime (but with the correct endianness) and let callers deal with it. Passing back strings from a C API just seems wrong.
These changes are bringing the hivexml program into a file system analysis suite that deals with many different file system types, each with their own timestamp recording quirks, and even some file formats which have yet more quirks. We think that ISO 8601 is the best umbrella output format, with an additional XML attribute noting the time granularity (like FAT's 2-second and 1-day granularities). That's why we're outputting strings in C, which, yes, feels wrong, but simplifies parsing outside of the scope of hivexml. We're dealing with the time presentation proactively. To prevent information loss, I could add decimal points to get the time reporting accurate to the 100-nanosecond level.
+/* Caller responsible for freeing returned char* if non-null. */ +char * +hivex_node_mtime (hive_h *h, hive_node_h node)
Add this function to the API (in generator/generator.ml, 'functions'). This will ensure that Perl, OCaml etc bindings + documentation are generated automatically for the function.
I hope that adding that function only entailed copy-pasting the list entry for node_name, assuming you accept the string return type. I have the next patch version formatted, will send after the above discussion's resolved. --Alex
Don't make it return a string. Have it return the original 64 bit Windows filetime instead (ie. RInt64) with the correct endianness, and let callers deal with it.
The rest seems fine to me, except that you'll need to push the filetime_to_8601 function into hivexml.c since it will now be getting a 64 bit filetime.
Rich.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
On Wed, Aug 10, 2011 at 01:24:05PM -0700, Alex Nelson wrote:
On Aug 10, 2011, at 12:55 , Richard W.M. Jones wrote:
On Wed, Aug 10, 2011 at 12:07:23PM -0700, Alex Nelson wrote:
The infrastructure for modified-time reporting has been essentially unused. These changes report the registry time by treating the time fields as Windows filetime fields stored in little-Endian (which means they can be treated as a single 64-bit little-Endian integer).
This patch adds the node_mtime function to the visitor API.
Nearly there. See my comments below.
@@ -93,6 +94,7 @@ struct hive_h { /* Fields from the header, extracted from little-endianness hell. */ size_t rootoffs; /* Root key offset (always an nk-block). */ size_t endpages; /* Offset of end of pages. */ + char *last_modified; /* mtime of base block. */
This field seems to be unused except in debugging messages? Unless you're going to use it, I'd just omit this field and all the code that generates and prints last_modified. There is a spot where it is printed: in lib/hivex.c:hivex__visit_node, "Report extra for hive's root node".
Hmmm so that callback is called twice for the root node? Stateful callbacks are complicated for callers to handle. This would be simpler if there were just two extra API functions, something like: int64_t hivex_last_modified (hive_h *); int64_t hivex_node_timestamp (hive_h *, hive_node_h); where the first returns the last_modified field for the whole hive and the second returns the timestamp for a particular node. You don't need to modify the hivex_visitor struct, because the node_start callback could just call hivex_node_timestamp.
This function is an oddity. You're taking a useful thing (64 bit Windows filetime) and converting it to a string. So callers are going to need to parse this string if they want to do anything useful with it. I think it's best just to return the 64 bit Windows filetime (but with the correct endianness) and let callers deal with it. Passing back strings from a C API just seems wrong.
These changes are bringing the hivexml program into a file system analysis suite that deals with many different file system types, each with their own timestamp recording quirks, and even some file formats which have yet more quirks. We think that ISO 8601 is the best umbrella output format, with an additional XML attribute noting the time granularity (like FAT's 2-second and 1-day granularities). That's why we're outputting strings in C, which, yes, feels wrong, but simplifies parsing outside of the scope of hivexml. We're dealing with the time presentation proactively.
Well I'm sure it slightly simplifies your use case, but it doesn't make things simpler for other calls of hivex. There are four things that we might return: (1) time_t: Native for Unix, but loses precision. (2) A string: Hard to parse from C. (3) time_t + fraction (eg. struct timespec). The generator can't currently handle this type. In any case it's problematic because it returns more precision than is really there. (4) A Windows filetime: Preserves complete fidelity in both directions.
Add this function to the API (in generator/generator.ml, 'functions'). This will ensure that Perl, OCaml etc bindings + documentation are generated automatically for the function.
I hope that adding that function only entailed copy-pasting the list entry for node_name, assuming you accept the string return type. I have the next patch version formatted, will send after the above discussion's resolved.
For hivex_node_timestamp: Have a look at "node_name" and "value_qword", and combine the two. For hivex_last_modified: Combine "value_qword" and "root". The generator takes most of the hard work out it. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
On Wed, Aug 10, 2011 at 01:24:05PM -0700, Alex Nelson wrote:
These changes are bringing the hivexml program into a file system analysis suite that deals with many different file system types, each with their own timestamp recording quirks, and even some file formats which have yet more quirks. We think that ISO 8601 is the best umbrella output format, with an additional XML attribute noting the time granularity (like FAT's 2-second and 1-day granularities). That's why we're outputting strings in C, which, yes, feels wrong, but simplifies parsing outside of the scope of hivexml. We're dealing with the time presentation proactively.
Reading this over again, I think you may be confusing how the hivex API/library returns the data and how hivexml displays the data. The two are completely different things. You can have hivexml displaying the data as an ISO 8601 string (in fact, I would say that is a very good choice). But that does not in any way require that the C API returns a string. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/
On Aug 10, 2011, at 14:17 , Richard W.M. Jones wrote:
On Wed, Aug 10, 2011 at 01:24:05PM -0700, Alex Nelson wrote:
These changes are bringing the hivexml program into a file system analysis suite that deals with many different file system types, each with their own timestamp recording quirks, and even some file formats which have yet more quirks. We think that ISO 8601 is the best umbrella output format, with an additional XML attribute noting the time granularity (like FAT's 2-second and 1-day granularities). That's why we're outputting strings in C, which, yes, feels wrong, but simplifies parsing outside of the scope of hivexml. We're dealing with the time presentation proactively.
Reading this over again, I think you may be confusing how the hivex API/library returns the data and how hivexml displays the data. The two are completely different things. You can have hivexml displaying the data as an ISO 8601 string (in fact, I would say that is a very good choice). But that does not in any way require that the C API returns a string.
Ah, understood. Ok, in that case I'm fine with changing things as you suggested. Next patch coming soon. --Alex
Rich.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/
On Wed, Aug 10, 2011 at 01:24:05PM -0700, Alex Nelson wrote:
These changes are bringing the hivexml program into a file system analysis suite that deals with many different file system types, each with their own timestamp recording quirks, and even some file formats which have yet more quirks. We think that ISO 8601 is the best umbrella output format, with an additional XML attribute noting the time granularity (like FAT's 2-second and 1-day granularities). That's why we're outputting strings in C, which, yes, feels wrong, but simplifies parsing outside of the scope of hivexml. We're dealing with the time presentation proactively.
Reading this over again, I think you may be confusing how the hivex API/library returns the data and how hivexml displays the data. The two are completely different things. You can have hivexml displaying the data as an ISO 8601 string (in fact, I would say that is a very good choice). But that does not in any way require that the C API returns a string.
Ah, understood. Ok, in that case I'm fine with changing things as you suggested. Next patch coming soon.
Please, please, please don't return the Windows time. Python does not have a library or function for dealing with the Windows time, but it does handle ISO 8601. And UNIX epoch. And a quick search for Perl doesn't show anything for Windows timestamp in the top 5, but also has a module for ISO8601. Elizabeth
On Thu, Aug 11, 2011 at 11:08:38PM -0700, Elizabeth Schweinsberg wrote:
On Wed, Aug 10, 2011 at 01:24:05PM -0700, Alex Nelson wrote:
These changes are bringing the hivexml program into a file system analysis suite that deals with many different file system types, each with their own timestamp recording quirks, and even some file formats which have yet more quirks. We think that ISO 8601 is the best umbrella output format, with an additional XML attribute noting the time granularity (like FAT's 2-second and 1-day granularities). That's why we're outputting strings in C, which, yes, feels wrong, but simplifies parsing outside of the scope of hivexml. We're dealing with the time presentation proactively.
Reading this over again, I think you may be confusing how the hivex API/library returns the data and how hivexml displays the data. The two are completely different things. You can have hivexml displaying the data as an ISO 8601 string (in fact, I would say that is a very good choice). But that does not in any way require that the C API returns a string.
Ah, understood. Ok, in that case I'm fine with changing things as you suggested. Next patch coming soon.
Please, please, please don't return the Windows time. Python does not have a library or function for dealing with the Windows time, but it does handle ISO 8601. And UNIX epoch. And a quick search for Perl doesn't show anything for Windows timestamp in the top 5, but also has a module for ISO8601.
You don't need a module for Windows filetime, since you can write a conversion function in a few lines of code: ---------------------------------------------------------------------- #!/usr/bin/python filetime = 128166372003061629L #filetime = 116444736000000000L # 1970-01-01 windows_tick = 10000000 sec_to_unix_epoch = 11644473600L time_t = filetime / windows_tick - sec_to_unix_epoch from datetime import datetime print datetime.fromtimestamp (time_t).strftime('%F %T') ---------------------------------------------------------------------- (adapted from http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-secon... ) Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top
participants (3)
-
Alex Nelson -
Elizabeth Schweinsberg -
Richard W.M. Jones