Jim's Depository

this code is not yet written
 

I’ve added a few features today.

  • There is now a ‘recent comments’ link under Contribute. It also has its own RSS feed, so I can watch for comments should the comment spammers get through.
  • Articles now have URLs constructed from their titles. I feel I could do better at this. I need to read some papers on automated abbreviators.
  • I fixed a nasty little problem where a sqlite3 “insert or replace” was whacking my password. Turns out that deletes the whole row on a replace, so you better be specifying all the field values.

And now 4 days later.

  • I now log much of the POST methods into my database. There is a distributed botnet trying to leave a comment on one article in this blog. I’m curious what they wish to say. I’d hate to miss out on a first contact situation.
  • Found a couple URLs that were missing their “.php”.  Apache was ok with this, lighttpd is not.
I have made contact with the robots. We should all be afraid. Thus far the robots have attempted to add these comments:
  • SEX
  • SEX
  • SEX SEX SEX LOVE
  • zubav1na-ps1h1chesk1e-bolezn1  except the digits 1 are supposed to be the letter 'i', I just didn't want to get indexed by it.
I suppose some filtering software will now block my site because it talks about sex.
More robot chatter:
  • fandango
  • tatuazh
So if a tattooed robot offers to dance the fandango with you, you should know it only wants sex.

I noticed that the Opera browser rocketed up to 38.4% of my hits. A quick dig of the logs shows that I am being drilled by bots that look to be trying to create link spam and masquerade as Opera browsers.

I suppose eventually they will have a human help them through the captcha and succeed. I have changed things about so untrusted users will get rel=nofollow tags on all their links. Maybe that will make them lose interest and go away.

I should probably make an RSS feed for comments while I’m at is so I notice when they get through the defenses.

While updating my systems monitoring I discovered Munin today. Munin captures a wide variety of system information and dumps it into RRD files to ultimately graph it at a central location.

The user interface doesn’t communicate problems well, but it provides the underlying data for you to answer those nagging questions that come up, like “When did our email traffic get so high?” or “Has that disk always run that hot?”

And my install notes:

  • Also install sensord, smartmontools, and ethtool when installing the munin-node package.
  • Make sure to punch a firewall rule for port 4949 from the central machine. The central machine does not want to get hung on one of the nodes.
  • Run sensors-detect after installing sensord.
  • Check your syslog after restarting sensord, a bunch of my Dells explode the daemon with a problem in the fan sensor, I have to take lm85 out of their modules. 
  • If you are a linux user with SATA drives, then when you install smartmontools, you must edit the /etc/smartd.conf file to comment out the DEVICESCAN line and put in a specific device line with the “-d ata”. May as well add a “-m you@email.address” while you are there so it will notify you. Don’t forget to edit /etc/defaults/smartmontools to enable the daemon.
  • In /etc/munin/plugins/ you might wish to take out the ntp* files, unless you care about that. Look where those links go and you will see other plugins you could link in. I added veth* ones for my virtual ethernets and the smart one to get all my drive failure data.
  • Again, if on linux with SATA drives you must edit the /etc/munin/plugin-conf.d/munin-node file in a couple of places to tell it to use “-d ata”

Sample bits of /etc/munin/plugin-conf.d/munin-node:

[hddtemp_smartctl] user root env.drives sda env.type_sda ata
[smart_sd*] user root env.smartargs -H -c -l error -l selftest -l selective -d ata

If you run sendmail as your mail server munin has 3 plugins that are in the base Debian install.  Link all 3 into your /etc/munin/plugins directory.   One, sendmail_mailqueue will work out of the box.  The other two depend on sendmail stats files that do not get created in a base Debian install.

To enable stats logging you must manually create the stats files.

# touch /var/lib/sendmail/sendmail.st
# touch /var/lib/sendmail/sm-client.st

Once these files have been created, with sendmail write permission, sendmail will start logging to them.  Gotta love sendmail, "If you create the log file for me, I will write to it."

You can test your mail statistics file creation manually with the mailstats command.
If you want to collect apache statistics with Munin you need to enable extended server status in apache.
ExtendedStatus On
<Location /server-status>
   SetHandler server-status
   Order deny,allow
   Deny from all
   Allow from 127.0.0.1
   Allow from munin-server.mydomain.com
</Location>

If your web server does not bind to localhost (127.0.0.1), you need to define the server status URL in your /etc/munin/plugin-conf.d/munin-node config file.
[apache_*]
env.url "http://servername.mydomain.com/server-status?auto"


After decades of backing up with dump I no longer do it. I suppose I got in the habit back in the days of tapes and just stayed through the disk years.

rsync is far better. I should have switched years ago.

  • Efficient incremental backups over networks, even for appending little bits to the ends of long files.
  • The backups are real directories of real files, easy to ferret about and find what you need.
  • Nifty trick to keep N days of backups without using N times the space, but still each tree looks like a snapshot.
  • Easy ssh based security.
  • You can do either a push or a pull depending on your security requirements.

The first thing to do is to read about the rsync –link-dest option. It lets you use hard links to share the contents of files across days of your backups.

The second thing to do is to decide on your backup strategy. For many machines I just keep 7 days of backups, it makes things easy. There is a backup for each day of the week and they overwrite when it wraps. For other applications where I have to go further back I rename directories, much like logrotate would.

The third thing to think about is what happens if your ssh key or rsync password is compromised. If you are running backups from cron, then there will be a machine readable key on your machine somehow. This may or may not be a danger depending how you have things secured. In my setups if you could get the key you could have gotten the data anyway. (Remember that your backup archive machine needs to be at least as secure as the live machine.)

Enough talking, more sample code:

Scenario #1: Many big machines, lots of bits to push, on the same secure network. We want to go fast. All the machines have different security policies.

I use push in this situation. There is a dedicated backup machine to receive and hold the bits. Only two trusted people have access to this machine. The backup machine runs an rsync daemon with a module for each host that lets the host write backups (only in its host specific area, write only). On each host there is a root cron job with the rsync password embedded to run the backup.

Sample backup script… cron these, offset their run times so keep contention down….

#!/bin/sh   
 HOST=`hostname` export
RSYNC_PASSWORD=1234567890abcdef12345678890abcdef   
 DAY=`date +%a | tr '[A-Z]' '[a-z]'`   
 case $DAY in   sun ) PDAY=sat ;;   mon ) PDAY=sun ;;   tue ) PDAY=mon
;;   wed ) PDAY=tue ;;   thu ) PDAY=wed ;;   fri ) PDAY=thu ;;   sat )
PDAY=fri ;; esac   
 OPTS="-aqH --link-dest=/$PDAY/ --no-devices --no-specials
--exclude=/proc/ --exclude=/sys/ --exclude=/dev/ --exclude=/tmp/
--delete"   
 time rsync $OPTS / $HOST@warehouse.federated.com::$HOST/$DAY

Sample module from rsyncd.conf…

[nexus]     auth users = nexus     secrets file = /etc/rsyncd.secrets   
 use chroot = yes     path = /warehouse/nexus     numeric ids = yes   
 list = no     read only = no     write only = yes     uid = 0     gid =
0     hosts allow = 111.222.33.44     hosts deny = *

Scenario #2: Offsite backup of Virtual Private Server

I have a machine that lives in a hosting facility. I have broken my first rule of service providers. They are not close enough for me to pop over and wrap my hands around someone if there is a problem, so I content myself with a full backup and the ability to be up and running at a new provider in 60 minutes if needed. I don’t want any credentials sitting on a machine at the hosting facility, so I do a pull in this situation. I also use ssh to protect my data in transit, but use the rsync daemon and modules on the far side to get better control, for instance to make it read only.

Cron job on my backup server (pardon the db_client related noise, that machine has to run dropbear instead of a more common ssh. But do notice that I have a –rsh to force a tunnel and a :: to use daemon mode and modules.)

#!/bin/bash

function saveone () { TAG=$1 SRC=$2

rm -rf vhosts/\$TAG.9 [ -d vhosts/\$TAG.8 ] && mv vhosts/\$TAG.8
vhosts/\$TAG.9 [ -d vhosts/\$TAG.7 ] && mv vhosts/\$TAG.7 vhosts/\$TAG.8
[ -d vhosts/\$TAG.6 ] && mv vhosts/\$TAG.6 vhosts/\$TAG.7 [ -d
vhosts/\$TAG.5 ] && mv vhosts/\$TAG.5 vhosts/\$TAG.6 [ -d vhosts/\$TAG.4
] && mv vhosts/\$TAG.4 vhosts/\$TAG.5 [ -d vhosts/\$TAG.3 ] && mv
vhosts/\$TAG.3 vhosts/\$TAG.4 [ -d vhosts/\$TAG.2 ] && mv vhosts/\$TAG.2
vhosts/\$TAG.3 [ -d vhosts/\$TAG.1 ] && mv vhosts/\$TAG.1 vhosts/\$TAG.2
[ -d vhosts/\$TAG.0 ] && mv vhosts/\$TAG.0 vhosts/\$TAG.1 [ -d
vhosts/\$TAG ] && mv vhosts/\$TAG vhosts/\$TAG.0
RSYNC\_PASSWORD=a8e261e7bac90138087f770caa5fea5b export RSYNC\_PASSWORD
OPTS="-aqHz --bwlimit=400 --exclude lost+found --exclude /tmp --exclude
/var/tmp --exclude /proc --exclude /sys --no-devices --no-specials
--delete" rsync \$OPTS --rsh "dbclient -l root -i .ssh/id\_archivist.db"
--link-dest=/home/archivist/vhosts/\$TAG.0/ \$SRC
/home/archivist/vhosts/\$TAG/ \> \~/\$TAG.log }

saveone studt-net rhth.lunarware.com::rhth

\~root/.ssh/authorized_keys on the virtual private server (Look at the bit in front of the ssh-dss, it restricts what that key can do, in particular it makes it only able to run the rsync daemon.)

no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,command="/rsync
--server --daemon ." ssh-dss
adfeadfaefasdfefeI\_DELETED\_MY\_KEY\_HEREadfasdfefadfae backups

rsyncd.conf

[machine] auth users = archivist secrets file = /etc/rsyncd.secrets path
= / numeric ids = yes list = no read only = yes write only = no uid = 0
gid = 0

There you have it. Reasonably safe backups. There is room for improvement, for instance, rather than coming straight into root with the restricted command it could be a different account and use “super” to run the command, and it should check the source IP and only work from the backup machine.

 

Eww, nasty double spacing of the code segments. I'll have to think about how to fix that. Safari put each line into its own div for some reason.

I’m entering into Virtual Hosting. Many of the pages and services I’ve run out of spare gear at FSG are now moved or moving to a virtual host. I chose VPSLink for their \$7/mo tiny server, then while moving my domain name pointers discovered that Gandi.net is starting a virtual hosting business with more RAM and disk for about the same price.

Living in a tiny slice of a virtual host is certainly different from living in a leftover 2GHz/1GB/500GB PC, fortunately I remember when 64M of RAM was huge so I think I’ll get by just fine.

  • postfix/dovecot in TLS, lighttpd+fastcgi+php5 all fits nicely.
  • TLS incurs a fair bit of RAM use.
  • CPU use is odd. I don’t have visibility to know if I am CPU bound. I don’t think I am, but I can’t see from inside my Xen box.
  • The machine is noticeably sluggish at things like an initial rsync backup offsite. Slow disk? Slow network? I’m not sure.
  • The total annual bill will cost less than the electricity to keep a PC powered up. Happy Earth Day!

I guess if you find the site gone and just this at archive.org you’ll know I’ve had a virtual hosting disaster.  I don’t foresee a problem. I don’t know what the hosting company does if a machine fails, but I keep a full backup every night in my closet so I’ll be ok. 

VHD is a nice format for exchanging disks. It doesn’t take up a lot of bytes with the empty space of a filesystem. But if you want to access the data without a virtual machine it can be a bit of a pain.

The attached program will expand most VHD files into a sparse image of the disk. You can then do whatever you wish with that, such as:

  • Use it with a virtual machine that takes raw images.
  • Convert it to a VMDK with qemu-img from the qemu folk. (Note: some people say qemu-img can read VHD format already. It didn’t work for me and the man page doesn’t mention it.)
  • Mount it on your Linux box, though you have to skip some bytes to get over the MBR and down to the first partition. Go look at Creating and using disk images mini-howto | Marc’s Realm and find “The dirty way”. Basically it boils down to trying all the plausible offsets until you find the right one:

    for ((i=0 ; $i < 10000 ; i=$i + 1)) ; do    mount -o loop,offset=$(($i * 512)) image.bootable /mnt && breakdone
    
  • Give it a sound grepping.

Note: I don’t support this program or even intend to run it ever again. I converted my data. If you need to convert your data, then enjoy.

Attachments

vhd2img.tar.gz 4515 bytes
Hello Jim,

This looks like just what the doctor ordered but it failed to compile for me:

Did I do something wrong or do you have any suggestions to help fix it?

Thanks much,
Patrick -> patrickkirchner   AT yahoo . com

Current Directory = /tmp/vhd2img
-->make
cc -g -MMD -Wstrict-prototypes -Wall -Werror    vhd2img.c   -o vhd2img
vhd2img.c:38:38: error: missing terminating ' character
vhd2img.c:59:37: error: missing terminating ' character
vhd2img.c:67:67: error: missing terminating ' character
make: *** [vhd2img] Error 1

Hello again,

Well, err, uh, duh!  I poked around in the .c file and removed the whole license preamble then it compiled just fine.  Sorry about that.  It worked great but the resulting .raw and converted .qcow2 file give me a BSOD when run with kvm.

Thanks,
Patrick.
I've fixed the compilation error and made it 64bit aware, I still need to test it anyway here the download link:

http://oss.netfarm.it/download/vhd2img-64bit-aware.tar.bz2

(use wget or direct link, a link from a page will cause 503)

the bsod is probably a 7b error (inaccessible boot device, you may need to fix system registry hive to use standard ide but it's tricky and you need a windows box or of course a working windows vm :D)

contact me if interested (sherpya@netfarm.it)

I successfully started a converted windows vista evaulation vhd I'm still fighting with the mouse and vnc :)
Well, it's been two years since anyone touched this, so no one probably cares, but I needed to do this in Windows, so I ported it.  Here it is for anyone that cares.  It should still be Linux compatible.  I built and used it with Visual Studio 2010 as an x64 app, but it should work fine any way that you build it.  Oh, and it's cpp now.

[code]
#ifdef WIN32
#define _CRT_SECURE_NO_WARNINGS
#include <winsock.h>
#include <stdint.h>
#define off_t uint64_t
#define fseeko _fseeki64
#define ftello _ftelli64 
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )
#else
/*
** some defines to make Linux happy with big files
*/
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE /* enable fseeko */
#include <arpa/inet.h>
#include <inttypes.h>
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

static int verbose = 1;

typedef struct {
uint16_t cylinders;
unsigned char heads;
unsigned char sectors;
} VHD_GEOMETRY;

PACK(
struct VHD_FOOTER{
char cookie[8];
uint32_t features;
uint32_t version;
uint64_t dataOffset;
uint32_t timeStamp;
char creatorApplication[4];
uint32_t creatorVersion;
char creatorOS[4];
uint64_t originalSize;
uint64_t currentSize;
VHD_GEOMETRY diskGeometry;
uint32_t diskType;
uint32_t checksum;
unsigned char uniqueId[16];
unsigned char savedState;
unsigned char padding[427];
});

PACK(
typedef struct{
char cookie[8];
uint64_t dataOffset;
uint64_t tableOffset;
uint32_t headerVersion;
uint32_t maxTableEntries;
uint32_t blockSize;
uint32_t checksum;
unsigned char parentUniqueId[16];
uint32_t parentTimeStamp;
uint32_t reserved1;
unsigned char parentUnicodeName[512];
PACK(
struct{
unsigned char platformCode[4];
uint32_t platformDataSpace;
uint32_t platformDataLength;
uint32_t reserved;
uint64_t platformDataOffset;
}) partentLocator[8];
unsigned char reserved2[256];
}) VHD_DYNAMIC;

/*
** network byte order to host, "double" i.e. long long
*/
static uint64_t ntoh64( uint64_t v)
{
if (htons(1) == 1) return v;
else {
return(uint64_t)ntohl( v&0x00000000ffffffff) << 32 | 
(uint64_t)ntohl( (v>>32)&0x00000000ffffffff);
}
}

static void dump_footer(VHD_FOOTER *footer)
{
int i;

fprintf(stderr,"==================== VHD Footer =====================\n");
fprintf(stderr,"%24s : '%8.8s'\n", "cookie", footer->cookie);
fprintf(stderr,"%24s : %08x\n", "features", ntohl(footer->features));
fprintf(stderr,"%24s : %08x\n", "version", ntohl(footer->version));
fprintf(stderr,"%24s : %016lx\n", "data offset", ntoh64(footer->dataOffset));
fprintf(stderr,"%24s : %08x\n", "time stamp", ntohl(footer->timeStamp));
fprintf(stderr,"%24s : '%4.4s'\n", "creator application", footer->creatorApplication);
fprintf(stderr,"%24s : %08x\n", "creator version", ntohl(footer->creatorVersion));
fprintf(stderr,"%24s : '%4.4s'\n", "creator os", footer->creatorOS);
fprintf(stderr,"%24s : %016lx\n", "original size", ntoh64(footer->originalSize));
fprintf(stderr,"%24s : %016lx\n", "current size", ntoh64(footer->currentSize));
fprintf(stderr,"%24s : %d\n", "cylinders", ntohs(footer->diskGeometry.cylinders));
fprintf(stderr,"%24s : %d\n", "heads", footer->diskGeometry.heads);
fprintf(stderr,"%24s : %d\n", "sectors", footer->diskGeometry.sectors);
fprintf(stderr,"%24s : %08x\n", "disk type", ntohl(footer->diskType));
fprintf(stderr,"%24s : %08x\n", "disk type", ntohl(footer->checksum));
fprintf(stderr,"%24s : ", "unique id");
for (i = 0; i < 16; i++) {
fprintf(stderr,"%02x%s", footer->uniqueId[i], "...-.-.-.-......"[i] == '-' ? "-" : "");
}
fprintf(stderr,"\n");
fprintf(stderr,"%24s : %02x\n", "saved state", footer->savedState);
/* should add the parent locator entries if I ever see a differenced file */
}

static void dump_dynamic(VHD_DYNAMIC *dynamic)
{
int i;

fprintf(stderr,"==================== VHD Dynamic =====================\n");
fprintf(stderr,"%24s : '%8.8s'\n", "cookie", dynamic->cookie);
fprintf(stderr,"%24s : %016lx\n", "data offset", ntoh64(dynamic->dataOffset));
fprintf(stderr,"%24s : %016lx\n", "table offset", ntoh64(dynamic->tableOffset));
fprintf(stderr,"%24s : %08x\n", "version", ntohl(dynamic->headerVersion));
fprintf(stderr,"%24s : %d\n", "max table entries", ntohl(dynamic->maxTableEntries));
fprintf(stderr,"%24s : %d\n", "block size", ntohl(dynamic->blockSize));
fprintf(stderr,"%24s : %08x\n", "checksum", ntohl(dynamic->checksum));
fprintf(stderr,"%24s : ", "parent unique id");
for (i = 0; i < 16; i++) {
fprintf(stderr,"%02x%s", dynamic->parentUniqueId[i], "...-.-.-.-......"[i] == '-' ? "-" : "");
}
fprintf(stderr,"\n");
fprintf(stderr,"%24s : %08x\n", "parent time stamp", ntohl(dynamic->parentTimeStamp));
fprintf(stderr,"%24s : %08x\n", "reserved", ntohl(dynamic->reserved1));
fprintf(stderr,"%24s : ", "parent unicode name");
for (i = 0; i < 512; i++) {
fprintf(stderr,"%02x %s", dynamic->parentUniqueId[i], (i%32)==31 ? "\n" : "");
}

}

uint32_t checksum(void *buffer, int nBytes) {
uint32_t checksum=0;
uint8_t *bytes = (uint8_t *)buffer;
int counter;
for(counter = 0; counter < nBytes; counter++) {
checksum += bytes[counter];
}
return ~checksum;
}

off_t get_footer(FILE *file, VHD_FOOTER *footer) {
uint64_t footerOffset = 0;
VHD_FOOTER returnFooter;
bool passedChecksum = false;

// First, try the footer at the end of the file.
if (fseeko(file, -512, SEEK_END) != 0) {
fprintf(stderr,"Failed to seek to the footer: %s\n", strerror(errno));
exit(1);
}
footerOffset = ftello(file);
if ( fread( &returnFooter, sizeof(returnFooter), 1, file) != 1) {
fprintf(stderr,"Failed to read footer of input file: %s\n", strerror(errno));
exit(1);
}
// Now, calculate the check-sum.
uint32_t uiChecksum = returnFooter.checksum;
returnFooter.checksum = 0;
uint32_t ourChecksum = checksum(&returnFooter, sizeof(returnFooter));
passedChecksum = uiChecksum == htonl(ourChecksum);
returnFooter.checksum = uiChecksum;
// If the first footer didn't pass the checksum, try the one at the end.
if(!passedChecksum) {
fprintf(stderr, "Checksum for footer failed, trying backup copy.\n");
if(fseeko(file, 0, SEEK_SET) != 0) {
fprintf(stderr,"Failed to backup footer of input file: %s\n", strerror(errno));
exit(1);
}
if ( fread( &returnFooter, sizeof(returnFooter), 1, file) != 1) {
fprintf(stderr,"Failed to read footer of input file: %s\n", strerror(errno));
exit(1);
}
// Now, calculate the check-sum.
uint32_t uiChecksum = returnFooter.checksum;
returnFooter.checksum = 0;
uint32_t ourChecksum = checksum(&returnFooter, sizeof(returnFooter));
passedChecksum = uiChecksum == htonl(ourChecksum);
returnFooter.checksum = uiChecksum;
}

if(passedChecksum) {
memcpy(footer, &returnFooter, sizeof(returnFooter));
} else {
fprintf(stderr,"Both the header and footer failed the checksum.\n");
exit(1);
}

return footerOffset;
}

int main( int argc, char **argv)
{
/*
** Make sure we were compiled correctly.
*/
assert( sizeof(VHD_FOOTER)==512);
assert( sizeof(VHD_DYNAMIC)==1024);

if (argc != 3) {
fprintf(stderr,"Usage: vhd2img INPUTFILE OUTPUTFILE\n");
exit(1);
}

FILE *in = fopen(argv[1],"rb");
if (!in) {
fprintf(stderr,"Failed to open input file '%s': %s\n", argv[1], strerror(errno));
exit(1);
}

FILE *out = fopen(argv[2],"wb");
if (!out) {
fprintf(stderr,"Failed to open output file '%s': %s\n", argv[1], strerror(errno));
exit(1);
}

VHD_FOOTER footer;
off_t footerOffset = get_footer(in, &footer);

if (verbose) dump_footer(&footer);

switch (htonl(footer.diskType)) {
case 4:
fprintf(stderr,"Differencing VHDs not supported.\n");
exit(1);
case 2:
/*
** Warning: untested code. I don't have one of these files.
*/
if (verbose) fprintf(stderr,"Processing fixed VHD...\n");
{
off_t o = 0;
char buf[512];

if (fseeko(in,(off_t)0,SEEK_SET) != 0) {
fprintf(stderr,"Failed to rewind input file: %s\n", strerror(errno));
exit(1);
}
for (o = 0; o < footerOffset; o += 512) {
if (fread( buf, 512, 1, in) != 1) {
fprintf(stderr,"Failed to read input file: %s\n", strerror(errno));
exit(1);
}
if (fwrite( buf, 512, 1, out) != 1) {
fprintf(stderr,"Failed to write output file: %s\n", strerror(errno));
exit(1);
}
}
fprintf(stderr,"Completed. %lld bytes written.\n", (long long)footerOffset);
return 0;
}
case 3:
if (verbose) fprintf(stderr,"Processing dynamic VHD...\n");
break;
default:
fprintf(stderr,"Disk type %08x not recognized.\n", htonl(footer.diskType));
exit(1);
}

VHD_DYNAMIC dynamic;

if (fseeko(in,ntoh64(footer.dataOffset),SEEK_SET) != 0) {
fprintf(stderr,"Failed to seek to dynamic disk header: %s\n", strerror(errno));
exit(1);
}

if (fread( &dynamic, sizeof(dynamic), 1, in) != 1) {
fprintf(stderr,"Failed ot read dynamic disk header: %s\n", strerror(errno));
exit(1);
}

if (verbose) dump_dynamic( &dynamic);

uint32_t blockBitmapSectorCount = (ntohl(dynamic.blockSize)/512/8+511)/512;
uint32_t sectorsPerBlock = ntohl(dynamic.blockSize)/512;
uint8_t *bitmap = (uint8_t *)malloc(blockBitmapSectorCount*512);

if (verbose) fprintf(stderr, "block bitmap sector count is %d\n", blockBitmapSectorCount);

if (fseeko(in,ntoh64(dynamic.tableOffset),SEEK_SET) != 0) {
fprintf(stderr,"Failed to seek to block allocation table: %s\n", strerror(errno));
exit(1);
}

uint32_t bats = ntohl( dynamic.maxTableEntries);
uint32_t *bat = (uint32_t *)calloc( sizeof(*bat) , bats);
if (fread( bat, sizeof(*bat), bats, in) != bats) {
fprintf(stderr,"Failed to read block allocation table: %s\n", strerror(errno));
exit(1);
}

if (fseeko(out, bats * sectorsPerBlock * 512 - 1,SEEK_SET) != 0) {
fprintf(stderr,"Failed to seek to the end of the image: %s\n", strerror(errno));
exit(1);
}
if (fwrite("", 1, 1, out) != 1) {
fprintf(stderr,"Failed to write the last byte of image: %s\n", strerror(errno));
exit(1);
}

unsigned int b;
uint32_t emptySectors = 0;
uint32_t usedSectors = 0;
uint32_t usedZeroes = 0;
char buf[512];

for (b = 0; b < bats; b++) {
if (ntohl(bat[b]) == 0xffffffff) {
emptySectors += sectorsPerBlock;
continue;   /* totally empty block */
}

uint64_t bo = ntohl(bat[b])*512LL;

if (bo > footerOffset) {
fprintf(stderr,"Bad block offset\n");
exit(1);
}
if (fseeko(in, bo, SEEK_SET) != 0) {
fprintf(stderr,"Failed to seek to data block bitmap: %s\n", strerror(errno));
exit(1);
}
if (fread( bitmap, 512*blockBitmapSectorCount, 1, in) != 1) {
fprintf(stderr,"Failed to read block bitmap(%ld): %s\n", bo, strerror(errno));
exit(1);
}

unsigned int s,k;
uint64_t opos = 0xffffffffffffffffLL;

if (fseeko(in, bo+512*blockBitmapSectorCount, SEEK_SET) != 0) {
fprintf(stderr,"Failed to seek to input sectors: %s\n", strerror(errno));
exit(1);
}
for (s = 0; s < sectorsPerBlock; s++) {
if (fread( buf, 512, 1, in) != 1) {
fprintf(stderr,"Failed to read sector: %s\n", strerror(errno));
exit(1);
}

int empty = 1;
for (k = 0; k < 512; k++) {
if (buf[k]) {
empty = 0;
break;
}
}

if ((bitmap[s/8] & (1<<(7-s%8))) == 0) {
emptySectors++;
if (!empty) {
fprintf(stderr,"block %d, sector %d should be empty and isn't.\n", b, s);
}
} else {
usedSectors++;
if (empty) {
usedZeroes++;
} else {
uint64_t pos = (b*sectorsPerBlock + s) * 512LL;
if (pos != opos) {
if (fseeko( out, pos, SEEK_SET) != 0) {
fprintf(stderr,"Failed to seek output file for sector %lld (block %d of %u,sector %d of %d): %s\n", 
(b*sectorsPerBlock + s) * 512LL, b,bats,s,sectorsPerBlock,
strerror(errno));
exit(1);
}
opos = pos;
}
if (fwrite( buf, 512, 1, out) != 1) {
fprintf(stderr,"Failed to write sector: %s\n", strerror(errno));
exit(1);
}
opos += 512LL;
}
}
}
}

if (verbose) {
fprintf(stderr,"%4.1f%% of the sectors were used.\n", 
100.0*usedSectors/(usedSectors+emptySectors));
fprintf(stderr,"%4.1f%% of the sectors are used after removing zeroes.\n", 
100.0*(usedSectors-usedZeroes)/(usedSectors+emptySectors));
}

return 0;
}
[/code]
Basically it boils down to trying all the plausible offsets until you find the right one
Wow! It is not needed! If you have cylinder-aligned partition, just skip 63*512 bytes. If you have megabyte-aligned, 1024*2*512.
I just built qemu from the source for OS X 10.7 on Mac. I had to use these flags to get the make to complete without an error on compiling darwin-user:
>>  ./configure --disable-darwin-user --disable-sdl --enable-cocoa --disable-kvm --disable-bsd-user

Then i directly converted a Windows 7 x64 created VHD image into a raw... well it's not done yet, but seems to be running fine. The latest qemu-img does understand the source file is .vhd and so the command I had to use is:
>>  qemu-img convert -O raw source.vhd output.raw

next step is to use dd to lay down the raw image onto a hdd partition... 
I haven't actually mounted the resulting image file yet, but the conversion seemed to run OK. If you want to check your output image file contents, for information on what is actually in there, you can use the "disktype" package
from sourceforge (disktype.sourceforge.net). That program is also included in your favorite package manager. It'll make it a bit easier to figure out where your
partitions are located. This is a sample output, using my newly converted image file. "disktype myimage.img"

Regular file, size 31.00 GiB (33286000640 bytes)
DOS/MBR partition map
Partition 1: 29.31 GiB (31473008640 bytes, 61470720 sectors from 2048, bootable)
  Type 0x83 (Linux)
  Ext3 file system
    UUID 09DE2E1E-2C2F-4378-A8E6-59C8723865C7 (DCE, v4)
    Volume size 29.31 GiB (31473008640 bytes, 7683840 blocks of 4 KiB)
Partition 2: 2.687 GiB (2884632576 bytes, 5634048 sectors from 61472768)
  Type 0x82 (Linux swap / Solaris)
  Linux swap, version 2, subversion 1, 4 KiB pages, little-endian
    Swap size 2.687 GiB (2884624384 bytes, 704254 pages of 4 KiB)

Sometimes you will find data structures to read that are encoded little-endian. You may find these functions useful. They aren’t as nightmarish to read as macros and presumably your compiler will do right by them.

#include <stdio.h>
#include <arpa/inet.h>/*
** Little endian to host, short
*/
static unsigned short ltohs( unsigned short v)
{
    if ( htons(1) == 1) {
    return ((v>>8)&0xff) | ((v<<8)&0xff00);
    } else return v;
}/*
** Little endian to host, Long
*/
static unsigned long ltohl( unsigned long v)
{
    if ( htons(1) == 1) {
    return ((v>>24)&0xff) | ((v>>8)&0xff00) | 
               ((v<<8)&0xff0000) | ((v << 24)&0xff000000);
    } else return v;
}/*
** Little endian to host, "double" i.e. long long
*/
static unsigned long long ltohd( unsigned long long v)
{
    if ( htons(1) == 1) {
    return (unsigned long long)ltohl( v&0x00000000ffffffff) << 32 | 
        (unsigned long long)ltohl( (v>>32)&0x00000000ffffffff);
    } else return v;
}int main( int argc, char **argv)
{
    unsigned char t1[] = { 0x01, 0x02, 0x03, 0x04, 
                           0x05, 0x06, 0x07, 0x08 };
    unsigned char t2[] = { 0xff, 0xfe, 0xfd, 0xfc, 
                           0xfb, 0xfa, 0xf9, 0xf8 };    printf("ltohs(01,02) = %04x\n", ltohs(*(unsigned short *)t1));
    printf("ltohs(ff,fe) = %04x\n", ltohs(*(unsigned short *)t2));
    printf("ltohl(01,02,03,04) = %08x\n", ltohl(*(unsigned long *)t1));
    printf("ltohl(ff,fe,fd,fc) = %08x\n", ltohl(*(unsigned long *)t2));
    printf("ltohd(01,02,03,04,05,06,07,08) = %016llx\n", 
           ltohd(*(unsigned long long *)t1));
    printf("ltohd(ff,fe,fd,fc,fb,fa,f9,f8) = %016llx\n", 
           ltohd(*(unsigned long long *)t2));
}

Temperature is the enemy of hard disks. Hot disks fail sooner.

If you have a well designed server with lightly loaded disks in a cool room your drives are probably running around 25°C(77°F). If you have a 4U box that you chucked full of drives you may find some of your drives running much hotter. For instance, I just found two running at 41°C(105°F) in a box with otherwise cool drives.

Drives are typically specified to operate up to 55°C(131°F), but their lives are shortened. Estimating from Google data it looks like your failure rate within three years is about triple for 45°C versus 25°C. In the third year, about 1 in 6 of the hot drives will fail.

So protect your servers:

  1. Know the problem: In debian land, install hddtemp and run it to see which drives are hot. Windows users might use DTemp.

    `` vev# for v in a b c d ;do hddtemp /dev/sd\$v ;done /dev/sda: ST3750640AS: 34°C /dev/sdb: ST3750640AS: 27°C /dev/sdc: ST3750640AS: 41°C /dev/sdd: ST3750640AS: 40°C vev# # I have two hot drives.

  2. Mitigate: If you have hot drives, move them around or adjust airflow, perhaps by making little cardstock air dams inside the server to cool the drives. Add a fan blowing on them if you have a large case.

  3. Monitor: Record their temperatures and check in on them once in a while to make sure things aren’t going badly.

Debian users might also want to install smartmontools which will track your S.M.A.R.T. data and notify you of problems.

Note: for SATA drives you will need a “-d ata” or it will misaddress them as SCSI drives and you need a “-m foo@example.com” if you want to be email notified.

Somewhere along the line the GNU C library and the BSD folks added variants of sprintf() that malloc() the resulting string. Very handy for avoiding an overflow situation and also makes for cleaner reading code.

`` int asprintf( char **ret, const char *format, …); int vasprintf( char **ret, const char *format, va_list ap);

Before:

`` char buf[32]; sprintf(buf, “s:%d m:%s”, code, msg); // stack overflow when msg is too long

… or …

``

char buf[1024];
snprintf( buf, sizeof(buf)-1, “s:%d m:%s”, code, msg); // fail oddly if we guessed wrong on the maximum buf size

After:

``

char *buf;
asprintf( &buf, “s:%d m:%s”, code, msg);
… free(buf);

Danger danger DANGER: This will not work on the tiny libc in OpenWRT and similar embedded systems. But at least you will get a link error during the build.

Now if only we had a scanf() that would do a realloc() on its pointers.

Oh look, there is a similar function for scanf().  You can do something like...

char *adj = 0;
sscanf(somestuff,"Some %as stuff", &adj);

... but only if you are using GNU libc. I got burned when I used this in a daemon and then moved it to OpenWRT where they uses a different libc.

Femtoblogger has just been rolled out to a corporate community of 150 users. Let the bug reports and feature requests roll.

more articles