public
Last active — forked from joemck/zenrecover.py

zenrecover.py, "undelete" version

  • Download Gist
zenrecover.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
#!/usr/bin/python
 
# Copyright 2007 by Tobia Conforto <tobia.conforto@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU General
# Public License as published by the Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
 
 
# Use this program to extract files from a disk image from a Creative Zen Xtra or Zen Vision M player.
# Unlike the zenrecover.py this is based on (https://gist.github.com/483969), this also finds deleted files
# and can extract most files after the player has been formatted. However, it doesn't distinguish between
# "songs" and "archives" areas on the player, and also extracts a few player system files.
#
# NOTE: Make sure it's set for the right player before you run it. Otherwise it won't find anything.
# See the "ZENVISION=..." line just after the comments.
 
 
# Versions: 0.1 2007-08-13 Initial release
# 0.2 2008-05-12 Small fixes for Zen Xtra models
# 0.3 2009-02-23 Zen Vision M compatible version (Leho Kraav <leho@kraav.com>)
# 0.2a 2010-07-20 Undid most of 0.3's changes to make it Zen Xtra-compatible again
# (because Tobia Conforto's site no longer has it and 0.3 is the only version of this
# script I can find anywhere)
# Modified it to use LRUCache from http://evan.prodromou.name/Software/Python/LRUCache
# because I couldn't find the module LRU that it used before.
# Added code to reencode path and filenames to UTF-8 since certain "weird" characters in
# filenames appearing on the device caused an unhandled exception.
# (Joe McKenzie <joemck85@gmail.com>)
# 0.2b 2010-07-21 Fixed problem when the filename field contains a path.
# Fixed problem with files added by XNJB for Max OS X
# Fixed a serious bug in the previous revision of this on Gist that created insane dirs
# Combined Xtra and Vision M tag checks to make it a bit easier to convert the script
# Removed use of LRUCache because:
# a. Most sectors are accessed exactly once, making the cache pointless.
# b. On my Slackware 13.1 AMD64 box with Python 2.6,
# with LRUCache: 2.5 MB/s
# without cache: 80 MB/s (d'oh...)
# (AMD Sempron 140, 2 GB RAM, 40 GB Zen Xtra disk image stored on one WD20EARS
# [2 TB WD Green drive], recovering to a directory on another WD20EARS)
# **** Cleaned up code and comments a bit, for instructions to make it work with a ****
# **** Zen Vision M, search this file for "visionm" ****
# (Joe McKenzie <joemck85@gmail.com>)
# 0.4 2011-02-07 Changed it to scan for anything that looks like an inode and recover it.
# Basically works like an "undelete" program now. It'll find some files even after you
# format your Zen. NOTE: May not recover ALL files after a format!!
# (Joe McKenzie <joemck85@gmail.com>)
# 0.5 2011-08-02 Removed parsing of directories in CFSInode.__init__ so it ignores dirs
# with errors in them. Now it should work better on damaged filesystems.
 
from __future__ import division
import sys, os, codecs, array, time, operator, getopt, re
 
# visionm: Set this to True for Zen Vision M, False for Zen Xtra
ZENVISION=True
 
class CFS:
if ZENVISION:
clusterSize = 0x8000
else:
clusterSize = 0x2000
 
def __init__(self, filename, offset = 0):
'''Filename and optional offset where the CFS filesystem begins
(offset of cluster -1, the one filled with 0xff)'''
self.image = file(filename)
self.offset = offset
 
def __getitem__(self, key):
'''Get the nth CFS cluster from the image and cache it for later usage.
Accepts simple slices of clusters, but doesn't process negative indices.
In any case it returns the requested data as a byte string.'''
if isinstance(key, slice):
cstart, cstop = key.start, key.stop
else:
cstart, cstop = key, key + 1
data = ''
for cluster in range(cstart, cstop):
self.image.seek(self.offset + (cluster + 1) * self.clusterSize)
data += self.image.read(self.clusterSize)
return data
 
def get_byteswapped_data(self, cluster):
'''Get the nth CFS cluster from the image, without caching it.
Swap the position of every two bytes and return it as an array object.
This method is designed for bulk file retrieving.'''
a = array.array('H')
self.image.seek(self.offset + (cluster + 1) * self.clusterSize)
a.fromfile(self.image, self.clusterSize // 2)
if not ZENVISION:
a.byteswap()
return a
 
def inode(self, cluster):
return CFSInode(self, cluster)
 
def pdp_uint32_xtra(data, offset = 0):
o2, o1, o4, o3 = map(ord, data[offset : offset + 4])
return (o1 << 24) | (o2 << 16) | (o3 << 8) | o4
 
def pdp_uint32_vision(data, offset = 0):
o4, o3, o2, o1 = map(ord, data[offset : offset + 4])
return (o1 << 24) | (o2 << 16) | (o3 << 8) | o4
 
def pdp_uint32(data, offset = 0):
if ZENVISION:
return pdp_uint32_vision(data, offset)
else:
return pdp_uint32_xtra(data, offset)
 
def pdp_uint16(data, offset = 0):
o2, o1 = map(ord, data[offset : offset + 2])
return (o1 << 8) | o2
 
def ucs2string(data, offset, length): # length in bytes
return codecs.utf_16_le_decode(data[offset : offset + length])[0]
 
def pdp_getbit(bitmap, bit_no):
return (pdp_uint32(bitmap, bit_no // 32 * 4) >> (bit_no % 32)) & 1
 
class CFSInode:
filename = '(no filename)'
filesize = 0
path = []
 
def __init__(self, cfs, cluster):
self.filename = '(no filename)'
self.filesize = 0
self.path = []
self.cluster = cluster
self.cfs = cfs
inode = cfs[cluster]
# reading misc flags and values
# print "pdp_uint: %x" % pdp_uint32(inode[4:8])
# print "cluster: %x" % cluster
assert pdp_uint32(inode[4:8]) == cluster # self-reference
self.serial = pdp_uint32(inode, 0x78)
# reading metadata
count_metadata = pdp_uint32(inode, 0x7c)
offset = 0x80
self.metadata = {}
for i in range(count_metadata):
assert pdp_uint16(inode, offset) == 3
length = pdp_uint16(inode, offset + 2)
tag = ucs2string(inode, offset + 4, 4)
self.metadata[tag] = inode[offset + 10 : offset + 10 + length]
# byte reordering issue, 07 -> 70, 0= -> =0, 0> -> >0
# but we cannot figure out where to get path info, tag '51' doesnt work
#...putting both the Xtra and Vision M tags here
# First one listed is for Zen Xtra, second/third are for Zen Vision M
# Remove the ones for the Nomad you don't have if the extra checks cause problems
if tag == '07' or tag == '70':
self.filename = unicode(ucs2string(inode, offset + 10, length - 2)).strip('\\/').encode("utf-8")
#I don't have a Vision M, so I can't test this... 51 or =0 might work...
elif tag == '0=' or tag == '51' or tag == '=0':
# handle UTF-8 properly
self.path = unicode(ucs2string(inode, offset + 10, length - 2)).encode("utf-8")
# split on both \ and /
self.path = re.split(r"[\\/]+", self.path.strip('\\/'))
elif tag == '0>' or tag == '>0':
self.filesize = pdp_uint32(inode, offset + 10)
offset += 10 + length
#if filename has / or \ in it, split and append those to the path
# (XNJB puts whole path in filename and leaves path blank)
if '/' in self.filename or '\\' in self.filename:
self.path.extend(re.split(r"[\\/]+", self.filename))
self.filename = self.path.pop()
print 'adjusted filename: %s' % self.filename
print 'adjusted path: %s' % repr(self.path)
# collecting flat list of data clusters
self.dataclusters = []
pointerclusters = []
for off in range(0x20, 0x4c + 1, 4):
c = pdp_uint32(inode, off)
if c != 0xFFFFFFFFL:
self.dataclusters.append(c)
second_class_chain = pdp_uint32(inode, 0x58)
if second_class_chain != 0xFFFFFFFFL:
pointerclusters.append(second_class_chain)
third_class_chain = pdp_uint32(inode, 0x64)
 
if ZENVISION:
clusterSize=0x8000
else:
clusterSize=0x2000
 
if third_class_chain != 0xFFFFFFFFL:
for off in range(0, clusterSize, 4):
c = pdp_uint32(cfs[third_class_chain], off)
if c == 0xFFFFFFFFL:
break
pointerclusters.append(c)
 
for pnt in pointerclusters:
for off in range(0, clusterSize, 4):
c = pdp_uint32(cfs[pnt], off)
if c == 0xFFFFFFFFL:
break
self.dataclusters.append(c)
 
def __getitem__(self, key):
'''Returns the given byte (or byte slice) from the file contents.'''
if isinstance(key, slice):
bstart, bstop = key.start, key.stop
else:
bstart, bstop = key, key + 1
cs = self.cfs.clusterSize
cstart = bstart // cs
cstop = (bstop - 1) // cs + 1
data = ''.join([ self.cfs[x] for x in self.dataclusters[cstart : cstop] ])
return data[bstart - cs * cstart : bstop - cs * cstart]
 
class CFSDirEntry:
def __init__(self, cfs, entrydata):
self.cluster = pdp_uint32(entrydata) # cluster no. of the inode
# length of full filename
self.len_filename = pdp_uint16(entrydata, 4)
# first 15 chars of filename
self.shortname = ucs2string(entrydata, 8, min(30, self.len_filename * 2))
 
if __name__ == '__main__':
 
# commandline arguments
optlist, args = getopt.gnu_getopt(sys.argv[1:], 'o:')
opts = dict(optlist)
offset = int(opts.get('-o', 20 * 2**20))
 
if len(args) != 2:
print 'Usage: zenrecover.py [-o OFFSET] DISK_OR_IMAGE OUTPUT_DIR'
print 'DISK_OR_IMAGE is the disk containing the filesystem, or an image thereof'
print 'OFFSET is the offset at which the filesystem starts (in bytes, default 20M)'
print 'OUTPUT_DIR is the directory in which to place the recovered files'
print
print '***NOTICE***: This version of zenrecover extracts everything it can find,'
print 'no matter what section it\'s in. This can and will extract songs, archives,'
print 'Zen system files like "sg00.log", and probably even some deleted stuff!'
sys.exit(1)
 
cfs = CFS(args[0], offset)
outdir = args[1]
 
try:
os.makedirs(outdir)
except OSError:
print "Can't create output directory \""+outdir+"\" -- make sure it doesn't already exist"
sys.exit()
 
lastfiles = [(1,1)] # timing of latest few files recovered (size in bytes, time in secs)
 
clust=4
while True:
isAnInode=False
try:
if pdp_uint32(cfs[clust][:4]) == 0x3bbe0ad9:
print "\n\nFound inode at cluster 0x%x" % clust
isAnInode=True
except ValueError:
print "Hit end of disk image, DONE."
print "cluster # "+str(clust)
break
if isAnInode:
# recover this inode if it's a single file
inode = cfs.inode(clust)
if (not inode.metadata) or inode.filesize == 0:
print " ignoring directory inode or empty file"
else:
t0 = time.time()
m=inode.metadata
print repr(m)
for j in m:
if len(m[j])==4:
print repr(j), pdp_uint32(m[j])
else:
print repr(j), repr(''.join([m[j][x] for x in range(0,len(m[j]),2)]))
print '\r%.1fMB/s "%s" (%.1fMB)\033[K' % (
operator.truediv(*map(sum, zip(*lastfiles))) / 2**20,
inode.filename[:50],
inode.filesize / 2**20),
sys.stdout.flush()
path = os.path.join(outdir, *inode.path)
try:
os.makedirs(path)
except:
pass
f = file(os.path.join(path, inode.filename), 'w')
remaining = inode.filesize
for c in inode.dataclusters:
if remaining >= cfs.clusterSize:
cfs.get_byteswapped_data(c).tofile(f)
else:
f.write(cfs.get_byteswapped_data(c).tostring()[:remaining])
remaining -= min(cfs.clusterSize, remaining)
f.close()
assert remaining == 0
if len(lastfiles) >= 32: #transfer speed is calculated on latest 32 files
lastfiles.pop(0)
lastfiles.append((inode.filesize, time.time() - t0))
# end of recover code
clust+=1

Do you have any suggestions on imaging the jukebox 3 disk? I have tried this with a basic software I bought and this script runs and recovers nothing. I suspect my image may not have been created properly. Any tips would be greatly appreciated.

btw, here's the output:

breathmint@ubuntu:/media/DATA2/JB3_images$ sudo python zenrecover.py ./jb3_20120414.img ./recover

Found inode at cluster 0x29
ignoring directory inode or empty file

Found inode at cluster 0x34
ignoring directory inode or empty file

Found inode at cluster 0x3f
ignoring directory inode or empty file

Found inode at cluster 0x4a
ignoring directory inode or empty file

Found inode at cluster 0x55
ignoring directory inode or empty file

Found inode at cluster 0x60
ignoring directory inode or empty file

Found inode at cluster 0x1edff
ignoring directory inode or empty file

Found inode at cluster 0x1ee03
ignoring directory inode or empty file

Found inode at cluster 0x1ee07
ignoring directory inode or empty file

Found inode at cluster 0x149350
ignoring directory inode or empty file

Found inode at cluster 0x195adf
ignoring directory inode or empty file

Found inode at cluster 0x195ae3
ignoring directory inode or empty file

Found inode at cluster 0x1b8095
ignoring directory inode or empty file

Found inode at cluster 0x1b8099
ignoring directory inode or empty file
Hit end of disk image, DONE.
cluster # 2439319

When making the image, what program did you use? I ask because it could
cause trouble if it's either storing in a special format or attempting to
find partitions. Creative-formatted disks use a proprietary format for both
the partition table and filesystem, though the filesystem is similar to
ext2.

Since you're on Linux, the imaging program I recommend is dd. Something
like:
sudo dd if=/dev/sdc of=~/jb3.img bs=1M
change /dev/sdc to whatever device the JB3 disk is assigned

However, I suspect your problem is that my program is designed for Zen
Vision M and Zen Xtra. Different Creative players use slightly different
on-disk formats. Trying the program in Zen Xtra mode might work better,
since I think the Xtra is closer to the JB3.

To do that, edit zenrecovery.py and change the line (near the top, just
below the comment block)
ZENVISION=True
to
ZENVISION=False

If it still finds nothing, please email me the first 40 MB or so of the
image:
sudo dd if=jb3_20120414.img bs=1M count=40 | bzip2 > first40.img.bz2

From that I can determine the correct offset and any differences in format.

On Fri, May 4, 2012 at 7:46 PM, breathmint <
reply@reply.github.com

wrote:

btw, here's the output:

breathmint@ubuntu:/media/DATA2/JB3_images$ sudo python zenrecover.py
./jb3_20120414.img ./recover

Found inode at cluster 0x29
ignoring directory inode or empty file

Found inode at cluster 0x34
ignoring directory inode or empty file

Found inode at cluster 0x3f
ignoring directory inode or empty file

Found inode at cluster 0x4a
ignoring directory inode or empty file

Found inode at cluster 0x55
ignoring directory inode or empty file

Found inode at cluster 0x60
ignoring directory inode or empty file

Found inode at cluster 0x1edff
ignoring directory inode or empty file

Found inode at cluster 0x1ee03
ignoring directory inode or empty file

Found inode at cluster 0x1ee07
ignoring directory inode or empty file

Found inode at cluster 0x149350
ignoring directory inode or empty file

Found inode at cluster 0x195adf
ignoring directory inode or empty file

Found inode at cluster 0x195ae3
ignoring directory inode or empty file

Found inode at cluster 0x1b8095
ignoring directory inode or empty file

Found inode at cluster 0x1b8099
ignoring directory inode or empty file
Hit end of disk image, DONE.
cluster # 2439319


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

Hey,

Thanks for the quick reply. I used a goofy app called Uneraser - and I
strongly suspect you're right about the format.

I'll give dd a try next, it was already on my shortlist of other things to
look in to. I'll also try using the other mode as well.

I'll let you know how it goes and thanks again for your superfast reply and
offer of further help if I have no luck.

Best Regards,
breathmint
On May 4, 2012 8:01 PM, "joemck" <
reply@reply.github.com>
wrote:

When making the image, what program did you use? I ask because it could
cause trouble if it's either storing in a special format or attempting to
find partitions. Creative-formatted disks use a proprietary format for both
the partition table and filesystem, though the filesystem is similar to
ext2.

Since you're on Linux, the imaging program I recommend is dd. Something
like:
sudo dd if=/dev/sdc of=~/jb3.img bs=1M
change /dev/sdc to whatever device the JB3 disk is assigned

However, I suspect your problem is that my program is designed for Zen
Vision M and Zen Xtra. Different Creative players use slightly different
on-disk formats. Trying the program in Zen Xtra mode might work better,
since I think the Xtra is closer to the JB3.

To do that, edit zenrecovery.py and change the line (near the top, just
below the comment block)
ZENVISION=True
to
ZENVISION=False

If it still finds nothing, please email me the first 40 MB or so of the
image:
sudo dd if=jb3_20120414.img bs=1M count=40 | bzip2 > first40.img.bz2

From that I can determine the correct offset and any differences in format.

On Fri, May 4, 2012 at 7:46 PM, breathmint <
reply@reply.github.com

wrote:

btw, here's the output:

breathmint@ubuntu:/media/DATA2/JB3_images$ sudo python zenrecover.py
./jb3_20120414.img ./recover

Found inode at cluster 0x29
ignoring directory inode or empty file

Found inode at cluster 0x34
ignoring directory inode or empty file

Found inode at cluster 0x3f
ignoring directory inode or empty file

Found inode at cluster 0x4a
ignoring directory inode or empty file

Found inode at cluster 0x55
ignoring directory inode or empty file

Found inode at cluster 0x60
ignoring directory inode or empty file

Found inode at cluster 0x1edff
ignoring directory inode or empty file

Found inode at cluster 0x1ee03
ignoring directory inode or empty file

Found inode at cluster 0x1ee07
ignoring directory inode or empty file

Found inode at cluster 0x149350
ignoring directory inode or empty file

Found inode at cluster 0x195adf
ignoring directory inode or empty file

Found inode at cluster 0x195ae3
ignoring directory inode or empty file

Found inode at cluster 0x1b8095
ignoring directory inode or empty file

Found inode at cluster 0x1b8099
ignoring directory inode or empty file
Hit end of disk image, DONE.
cluster # 2439319


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

Not that it matters to my program, but an observation: Your image has an
MBR with a Windows XP bootloader and no partitions at the start. This most
likely came from inadvertently "initializing" the disk in Windows. It
overwrote the Creative partition table, which will most likely make the
disk unusable in a JB3 device. However, my program doesn't care because it
uses hardcoded per-device offsets for the main partition. (If needed, I can
likely reconstruct the Creative partition table since the JB3 seems very
similar to Zen Xtra.)

I'd like to see the other directory inodes that aren't in the first 40 MB
you sent me.
for i in 0x1edff 0x1ee03 0x1ee07 0x149350 0x195adf 0x195ae3 0x1b8095
0x1b8099; do dd if=jb3.img bs=8192 count=16 skip=$((2561 + $i)) >>
inodes.dat;done
bzip2 inodes.dat
and send me the resulting file.

So far, when I try to parse the filesystem by hand, I get one empty root
directory per section. I see two possibilities here:
1. JB3's file inodes aren't being recognized by my program, and the
directory ones containing actual files are beyond the first 40 MB.
2. The disk was formatted, and unlike my Zen Xtra, JB3 might actually zero
the disk when formatting. IF this is the case, nothing can be recovered :(

Two things you can do to check for this:

  • Try using PhotoRec. It's a data recovery program that scans for bits of data that look like known file formats. If it recovers anything meaningful, the disk hasn't been zeroed. (Obviously it'll extract corrupted files if there's any fragmentation, since it doesn't parse any filesystem structures.)
  • Zip your entire JB3 image and look at the file size. "Fast" compression mode recommended, as with gzip -1. This will take a while since it's 20 GB. If the resulting archive is something piddly like 120 MB, the disk was probably zeroed. (For reference, gzip -1 compresses 1 GB of zeroes to 4.5 MB; 1 GB of MP3 files will compress to about 1 GB.) easiest way to do that (displays size in bytes when done): gzip -1 < jb3.img | wc -c or with progress meter (requires "pv" package - pv is cat with a progress bar): pv jb3.img | gzip -1 | wc -c

On Sat, May 5, 2012 at 1:52 PM, breathmint <
reply@reply.github.com

wrote:

Hey Joe,

I tried making the image with dd and got the same result, unfortunately. I
was already using ZENVISION=False. Mind checking out the first 40 MB as you
previously mentioned?

http://np.cyanidebreathmint.net/downloads/joemck/first40.img.bz2

Thanks again for your help,
breathmint

On Fri, May 4, 2012 at 8:01 PM, joemck <
reply@reply.github.com

wrote:

When making the image, what program did you use? I ask because it could
cause trouble if it's either storing in a special format or attempting to
find partitions. Creative-formatted disks use a proprietary format for
both
the partition table and filesystem, though the filesystem is similar to
ext2.

Since you're on Linux, the imaging program I recommend is dd. Something
like:
sudo dd if=/dev/sdc of=~/jb3.img bs=1M
change /dev/sdc to whatever device the JB3 disk is assigned

However, I suspect your problem is that my program is designed for Zen
Vision M and Zen Xtra. Different Creative players use slightly different
on-disk formats. Trying the program in Zen Xtra mode might work better,
since I think the Xtra is closer to the JB3.

To do that, edit zenrecovery.py and change the line (near the top, just
below the comment block)
ZENVISION=True
to
ZENVISION=False

If it still finds nothing, please email me the first 40 MB or so of the
image:
sudo dd if=jb3_20120414.img bs=1M count=40 | bzip2 > first40.img.bz2

From that I can determine the correct offset and any differences in
format.

On Fri, May 4, 2012 at 7:46 PM, breathmint <
reply@reply.github.com

wrote:

btw, here's the output:

breathmint@ubuntu:/media/DATA2/JB3_images$ sudo python zenrecover.py
./jb3_20120414.img ./recover

Found inode at cluster 0x29
ignoring directory inode or empty file

Found inode at cluster 0x34
ignoring directory inode or empty file

Found inode at cluster 0x3f
ignoring directory inode or empty file

Found inode at cluster 0x4a
ignoring directory inode or empty file

Found inode at cluster 0x55
ignoring directory inode or empty file

Found inode at cluster 0x60
ignoring directory inode or empty file

Found inode at cluster 0x1edff
ignoring directory inode or empty file

Found inode at cluster 0x1ee03
ignoring directory inode or empty file

Found inode at cluster 0x1ee07
ignoring directory inode or empty file

Found inode at cluster 0x149350
ignoring directory inode or empty file

Found inode at cluster 0x195adf
ignoring directory inode or empty file

Found inode at cluster 0x195ae3
ignoring directory inode or empty file

Found inode at cluster 0x1b8095
ignoring directory inode or empty file

Found inode at cluster 0x1b8099
ignoring directory inode or empty file
Hit end of disk image, DONE.
cluster # 2439319


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

breathmint

breathmint


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

Hi Again,

I have attached the file you requested. Also I tried the compression test
you mentioned, and didn't realize until the end there wouldn't be an output
file - just a count. If I interpreted the output right (assuming it was
file size in bytes): then it was something like (number)/(1024)^3 = 13.x GB

  • so I'm guessing there was no zeroes overwrite.

I tried using photorec, but once it started I didn't want to work on the
mounted drive and possibly overwrite anything, and I couldn't figure out
how to work with the image. So I just stuck with the compression test.

Finally, I should probably mention this isn't a deleted file I'm trying to
recover. I use my JB3 to make live concert recordings. At the end of my
most recent recording the battery died as it was "saving" the file at the
end (initiated when you press the stop button). I realize that this
probably causes some indexing issues, but the raw data should all be there.
This leads me to believe that at some point using photorec might be helpful

  • but anything you can suggest to help is greatly appreciated.

Thanks,
breathmint

On Sat, May 5, 2012 at 4:56 PM, joemck <
reply@reply.github.com

wrote:

Not that it matters to my program, but an observation: Your image has an
MBR with a Windows XP bootloader and no partitions at the start. This most
likely came from inadvertently "initializing" the disk in Windows. It
overwrote the Creative partition table, which will most likely make the
disk unusable in a JB3 device. However, my program doesn't care because it
uses hardcoded per-device offsets for the main partition. (If needed, I can
likely reconstruct the Creative partition table since the JB3 seems very
similar to Zen Xtra.)

I'd like to see the other directory inodes that aren't in the first 40 MB
you sent me.
for i in 0x1edff 0x1ee03 0x1ee07 0x149350 0x195adf 0x195ae3 0x1b8095
0x1b8099; do dd if=jb3.img bs=8192 count=16 skip=$((2561 + $i)) >>
inodes.dat;done
bzip2 inodes.dat
and send me the resulting file.

So far, when I try to parse the filesystem by hand, I get one empty root
directory per section. I see two possibilities here:
1. JB3's file inodes aren't being recognized by my program, and the
directory ones containing actual files are beyond the first 40 MB.
2. The disk was formatted, and unlike my Zen Xtra, JB3 might actually zero
the disk when formatting. IF this is the case, nothing can be recovered :(

Two things you can do to check for this:

  • Try using PhotoRec. It's a data recovery program that scans for bits of data that look like known file formats. If it recovers anything meaningful, the disk hasn't been zeroed. (Obviously it'll extract corrupted files if there's any fragmentation, since it doesn't parse any filesystem structures.)
  • Zip your entire JB3 image and look at the file size. "Fast" compression mode recommended, as with gzip -1. This will take a while since it's 20 GB. If the resulting archive is something piddly like 120 MB, the disk was probably zeroed. (For reference, gzip -1 compresses 1 GB of zeroes to 4.5 MB; 1 GB of MP3 files will compress to about 1 GB.) easiest way to do that (displays size in bytes when done): gzip -1 < jb3.img | wc -c or with progress meter (requires "pv" package - pv is cat with a progress bar): pv jb3.img | gzip -1 | wc -c

On Sat, May 5, 2012 at 1:52 PM, breathmint <
reply@reply.github.com

wrote:

Hey Joe,

I tried making the image with dd and got the same result, unfortunately.
I
was already using ZENVISION=False. Mind checking out the first 40 MB as
you
previously mentioned?

http://np.cyanidebreathmint.net/downloads/joemck/first40.img.bz2

Thanks again for your help,
breathmint

On Fri, May 4, 2012 at 8:01 PM, joemck <
reply@reply.github.com

wrote:

When making the image, what program did you use? I ask because it could
cause trouble if it's either storing in a special format or attempting
to
find partitions. Creative-formatted disks use a proprietary format for
both
the partition table and filesystem, though the filesystem is similar to
ext2.

Since you're on Linux, the imaging program I recommend is dd. Something
like:
sudo dd if=/dev/sdc of=~/jb3.img bs=1M
change /dev/sdc to whatever device the JB3 disk is assigned

However, I suspect your problem is that my program is designed for Zen
Vision M and Zen Xtra. Different Creative players use slightly
different
on-disk formats. Trying the program in Zen Xtra mode might work better,
since I think the Xtra is closer to the JB3.

To do that, edit zenrecovery.py and change the line (near the top, just
below the comment block)
ZENVISION=True
to
ZENVISION=False

If it still finds nothing, please email me the first 40 MB or so of the
image:
sudo dd if=jb3_20120414.img bs=1M count=40 | bzip2 > first40.img.bz2

From that I can determine the correct offset and any differences in
format.

On Fri, May 4, 2012 at 7:46 PM, breathmint <
reply@reply.github.com

wrote:

btw, here's the output:

breathmint@ubuntu:/media/DATA2/JB3_images$ sudo python zenrecover.py
./jb3_20120414.img ./recover

Found inode at cluster 0x29
ignoring directory inode or empty file

Found inode at cluster 0x34
ignoring directory inode or empty file

Found inode at cluster 0x3f
ignoring directory inode or empty file

Found inode at cluster 0x4a
ignoring directory inode or empty file

Found inode at cluster 0x55
ignoring directory inode or empty file

Found inode at cluster 0x60
ignoring directory inode or empty file

Found inode at cluster 0x1edff
ignoring directory inode or empty file

Found inode at cluster 0x1ee03
ignoring directory inode or empty file

Found inode at cluster 0x1ee07
ignoring directory inode or empty file

Found inode at cluster 0x149350
ignoring directory inode or empty file

Found inode at cluster 0x195adf
ignoring directory inode or empty file

Found inode at cluster 0x195ae3
ignoring directory inode or empty file

Found inode at cluster 0x1b8095
ignoring directory inode or empty file

Found inode at cluster 0x1b8099
ignoring directory inode or empty file
Hit end of disk image, DONE.
cluster # 2439319


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

breathmint

breathmint


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

breathmint

I don't see any attachment. I think Github discards them when you reply
using @reply.github.com addresses. Either email me directly (joemck85 at
gmail) or upload it elsewhere and give me the link.

Actually, now I am seeing a couple files. I had been mostly concentrating
on /songs and /archives before. In /recordings, I see recfile1 and
recfile2. (I can't tell if these are files or directories since I don't
have their inodes, which are in the file you tried to send.) Can you tell
if either of these is something you care about?

What format does the JB3 record in?

BTW, since you seem to have somehow gotten a WinXP bootsector on the disk,
I'll be interested to hear what happens when you put it back into the JB3
once we get the recordings off it.

On Sun, May 6, 2012 at 12:30 PM, breathmint <
reply@reply.github.com

wrote:

Hi Again,

I have attached the file you requested. Also I tried the compression test
you mentioned, and didn't realize until the end there wouldn't be an output
file - just a count. If I interpreted the output right (assuming it was
file size in bytes): then it was something like (number)/(1024)^3 = 13.x GB

  • so I'm guessing there was no zeroes overwrite.

I tried using photorec, but once it started I didn't want to work on the
mounted drive and possibly overwrite anything, and I couldn't figure out
how to work with the image. So I just stuck with the compression test.

Finally, I should probably mention this isn't a deleted file I'm trying to
recover. I use my JB3 to make live concert recordings. At the end of my
most recent recording the battery died as it was "saving" the file at the
end (initiated when you press the stop button). I realize that this
probably causes some indexing issues, but the raw data should all be there.
This leads me to believe that at some point using photorec might be helpful

  • but anything you can suggest to help is greatly appreciated.

Thanks,
breathmint

On Sat, May 5, 2012 at 4:56 PM, joemck <
reply@reply.github.com

wrote:

Not that it matters to my program, but an observation: Your image has an
MBR with a Windows XP bootloader and no partitions at the start. This
most
likely came from inadvertently "initializing" the disk in Windows. It
overwrote the Creative partition table, which will most likely make the
disk unusable in a JB3 device. However, my program doesn't care because
it
uses hardcoded per-device offsets for the main partition. (If needed, I
can
likely reconstruct the Creative partition table since the JB3 seems very
similar to Zen Xtra.)

I'd like to see the other directory inodes that aren't in the first 40 MB
you sent me.
for i in 0x1edff 0x1ee03 0x1ee07 0x149350 0x195adf 0x195ae3 0x1b8095
0x1b8099; do dd if=jb3.img bs=8192 count=16 skip=$((2561 + $i)) >>
inodes.dat;done
bzip2 inodes.dat
and send me the resulting file.

So far, when I try to parse the filesystem by hand, I get one empty root
directory per section. I see two possibilities here:
1. JB3's file inodes aren't being recognized by my program, and the
directory ones containing actual files are beyond the first 40 MB.
2. The disk was formatted, and unlike my Zen Xtra, JB3 might actually
zero
the disk when formatting. IF this is the case, nothing can be recovered
:(

Two things you can do to check for this:

  • Try using PhotoRec. It's a data recovery program that scans for bits of data that look like known file formats. If it recovers anything meaningful, the disk hasn't been zeroed. (Obviously it'll extract corrupted files if there's any fragmentation, since it doesn't parse any filesystem structures.)
  • Zip your entire JB3 image and look at the file size. "Fast" compression mode recommended, as with gzip -1. This will take a while since it's 20 GB. If the resulting archive is something piddly like 120 MB, the disk was probably zeroed. (For reference, gzip -1 compresses 1 GB of zeroes to 4.5 MB; 1 GB of MP3 files will compress to about 1 GB.) easiest way to do that (displays size in bytes when done): gzip -1 < jb3.img | wc -c or with progress meter (requires "pv" package - pv is cat with a progress bar): pv jb3.img | gzip -1 | wc -c

On Sat, May 5, 2012 at 1:52 PM, breathmint <
reply@reply.github.com

wrote:

Hey Joe,

I tried making the image with dd and got the same result,
unfortunately.
I
was already using ZENVISION=False. Mind checking out the first 40 MB as
you
previously mentioned?

http://np.cyanidebreathmint.net/downloads/joemck/first40.img.bz2

Thanks again for your help,
breathmint

On Fri, May 4, 2012 at 8:01 PM, joemck <
reply@reply.github.com

wrote:

When making the image, what program did you use? I ask because it
could
cause trouble if it's either storing in a special format or
attempting
to
find partitions. Creative-formatted disks use a proprietary format
for
both
the partition table and filesystem, though the filesystem is similar
to
ext2.

Since you're on Linux, the imaging program I recommend is dd.
Something
like:
sudo dd if=/dev/sdc of=~/jb3.img bs=1M
change /dev/sdc to whatever device the JB3 disk is assigned

However, I suspect your problem is that my program is designed for
Zen
Vision M and Zen Xtra. Different Creative players use slightly
different
on-disk formats. Trying the program in Zen Xtra mode might work
better,
since I think the Xtra is closer to the JB3.

To do that, edit zenrecovery.py and change the line (near the top,
just
below the comment block)
ZENVISION=True
to
ZENVISION=False

If it still finds nothing, please email me the first 40 MB or so of
the
image:
sudo dd if=jb3_20120414.img bs=1M count=40 | bzip2 > first40.img.bz2

From that I can determine the correct offset and any differences in
format.

On Fri, May 4, 2012 at 7:46 PM, breathmint <
reply@reply.github.com

wrote:

btw, here's the output:

breathmint@ubuntu:/media/DATA2/JB3_images$ sudo python
zenrecover.py
./jb3_20120414.img ./recover

Found inode at cluster 0x29
ignoring directory inode or empty file

Found inode at cluster 0x34
ignoring directory inode or empty file

Found inode at cluster 0x3f
ignoring directory inode or empty file

Found inode at cluster 0x4a
ignoring directory inode or empty file

Found inode at cluster 0x55
ignoring directory inode or empty file

Found inode at cluster 0x60
ignoring directory inode or empty file

Found inode at cluster 0x1edff
ignoring directory inode or empty file

Found inode at cluster 0x1ee03
ignoring directory inode or empty file

Found inode at cluster 0x1ee07
ignoring directory inode or empty file

Found inode at cluster 0x149350
ignoring directory inode or empty file

Found inode at cluster 0x195adf
ignoring directory inode or empty file

Found inode at cluster 0x195ae3
ignoring directory inode or empty file

Found inode at cluster 0x1b8095
ignoring directory inode or empty file

Found inode at cluster 0x1b8099
ignoring directory inode or empty file
Hit end of disk image, DONE.
cluster # 2439319


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

breathmint

breathmint


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

breathmint


Reply to this email directly or view it on GitHub:
https://gist.github.com/816115

Firstly, thanks for building/sharing this code, it looks like exactly the thing I need. I had a hard-drive failure with my music collection and the only backup I have is on my Creative Zen Nomad 30Gb player.

I need to run this on Windows 7. Which version of Python do you recommend?
Also I am new to Python, thus what would be the command line including directory structure eg ($ python e:\zenrecover\zenrecover.py e:\music i:) ?

Regards

Mark

I have come back to it and see that my disk is "not Initialized" in windows. What should I do? I'm hesitant to initialize it as I imagine I may be able to recover less of my music. Is this the case?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.