mboost-dp1

Simulere skrivning til read/only blockdevice under Linux


Gå til bund
Gravatar #1 - kasperd
21. feb. 2011 19:41
Er her nogen som kender en måde at simulere skrivninger til et readonly blockdevice under Linux? Jeg er interesseret i noget der midlertidigt gemmer det der bliver skrevet i RAM uden at skrive det til det underliggende device, sådan at ændringerne vil være tilgængelige indtil jeg unmounter filsystemet.

Grunden til at jeg er interesseret i det er, at jeg gerne vil kunne mounte et ext3 filsystem og læse indholdet på en måde som garanterer, at der ikke bliver skrevet til devicet. At blot mounte det med -o ro er ikke godt nok fordi det stadig vil lave et journal replay. Og hvis jeg rent faktisk sørger for at skrivninger er blokeret på bloklaget, så vil mount kommandoen fejle fordi filsystemet ikke kan lave et journal replay.

Det burde kunne kodes på et par timer med lidt hacks gennem nbd driveren, men jeg er ikke lige i humør til at bruge tid på at kode det, hvis der allerede findes noget.
Gravatar #2 - myplacedk
21. feb. 2011 21:14
Der er fx. "UnionFS", som fungerer som et lag oven på et (eller flere) andre filsystemer. Det betyder bla. at du kan mounte et readonly filsystem som read-write, og forskellene lagres et andet sted.
Det bliver bla. brugt til Ubuntu's livecd, hvor filsystemet er på en CD (fysisk readonly), men du kan stadig lave ændringer som så gemmes i en fil uden for readonly filsystemet.

Men om det så er muligt at mounte et ext3 filsystem i unionfs, uden at røre ved journalen, det aner jeg ikke.

En anden vej du måske kan gå er, at bruge en "forensic distribution". Det er typisk live-CD'er som er specielt designet til at kigge på indholdet af en harddisk, uden at ændre en byte.
Jeg kan ikke lige huske et godt navn, så jeg kan kun henvise til Google.
Gravatar #3 - kasperd
21. feb. 2011 23:44
/* Copyright (C) 2003-2011  Kasper Dupont */

#define _GNU_SOURCE
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64

#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#define __KERNEL__
#include <asm/types.h>
#undef __KERNEL__
#include <linux/major.h>
#include <linux/nbd.h>
#include <assert.h>
#include <netinet/in.h> /* For byteorder conversion */
#include <linux/fs.h>

int nbd_socket(char *device, uint64_t size)
{
int mysockets[2];
int fd=open(device,O_RDONLY);
if (fd!=-1) {
if (socketpair(PF_UNIX,SOCK_STREAM,0,mysockets)!=-1) {
if (!ioctl(fd,NBD_SET_SOCK,mysockets[1])) {
ioctl(fd,NBD_SET_BLKSIZE,4096);
ioctl(fd,NBD_SET_SIZE_BLOCKS,size/4096);
printf("Size: %llu\n", size);
close(mysockets[1]);
if(!fork()) {
close(mysockets[0]);
ioctl(fd,NBD_DO_IT);
_exit(0);
}
close(fd);
return mysockets[0];
}
close(mysockets[0]);
close(mysockets[1]);
}
close(fd);
}
return -1;
}

int detach(const char *device)
{
int fd=open(device,O_RDONLY);
int r;
if (fd==-1) return -1;
r=ioctl(fd,NBD_CLEAR_SOCK);
close(fd);
return r;
}

#define BLOCK_BITS 12
#define SHADOW_BLOCK_SIZE (1<<BLOCK_BITS)

struct node {
struct node *(p[16]);
} *root = NULL;

char **get_pointer(uint64_t number)
{
int i;
struct node **p=&root;
for (i=15;i>=0;--i) {
if (!*p) {
assert(*p=calloc(sizeof(struct node),1));
}
p=(*p)->p+((number>>(4*i))&15);
}
return p;
}

void main_daemon(int fd, int backing_fd)
{
struct nbd_request req;
struct nbd_reply rep;
rep.magic=htonl(NBD_REPLY_MAGIC);
rep.error=0;
while(1) {
/* Where is ntohll? */
uint64_t from;
uint32_t len;
assert(read(fd,&req,sizeof(req))==sizeof(req));
from=ntohl(req.from>>32)|(((uint64_t)ntohl(req.from))<<32);
len=ntohl(req.len);
printf("%s %lld:%d\n",req.type?"write":"read",from,len);
assert(ntohl(req.magic)==NBD_REQUEST_MAGIC);
memcpy(rep.handle,req.handle,sizeof(rep.handle));

if (!req.type) {
send(fd,&rep,sizeof(rep),0);
}
while (len) {
char **p=get_pointer(from >> BLOCK_BITS);
int block_len = SHADOW_BLOCK_SIZE - (len & (SHADOW_BLOCK_SIZE - 1));
if (block_len > len) {
block_len=len;
}

if (req.type) {
int r;
assert(block_len>0);
if (!*p) {
assert(*p=malloc(SHADOW_BLOCK_SIZE));
assert(pread(backing_fd, *p, SHADOW_BLOCK_SIZE, from & (-SHADOW_BLOCK_SIZE))==SHADOW_BLOCK_SIZE);
}
assert((r=read(fd,(*p)+(from&(SHADOW_BLOCK_SIZE-1)),block_len))>0);
len-=r;
from+=r;
} else {
if (*p) {
send(fd,(*p)+(from&(SHADOW_BLOCK_SIZE-1)),block_len,0);
} else {
char buffer[4096];
assert(pread(backing_fd, buffer, block_len, from) == block_len);
send(fd,buffer,block_len,0);
}
len-=block_len;
from+=block_len;
}
}
if (req.type) {
send(fd,&rep,sizeof(rep),0);
}
}
}

uint64_t device_size(int fd)
{
struct stat st;
unsigned long blocks;

fstat(fd, &st);

if (S_ISREG(st.st_mode)) {
return st.st_size;
}

if (S_ISBLK(st.st_mode)) {
ioctl(fd, BLKGETSIZE, &blocks);
return ((uint64_t)blocks)<<9;
}

fprintf(stderr, "Not device or regular file\n");
exit(1);
}


int main(int argc, char ** argv)
{
int fd, backing_fd;
struct hd_geometry;

if (argc != 3) {
printf("Usage: %s <nbddevice> <file>\n"
" %s -d <nbddevice>\n",
argv[0],argv[0]);
return 1;
}

if (!strcmp(argv[1],"-d")) {
if (detach(argv[2])) {
perror(argv[2]);
return 1;
}
return 0;
}

backing_fd=open(argv[2], O_RDONLY);
if (backing_fd == -1) {
perror(argv[2]);
return 1;
}

fd=nbd_socket(argv[1], device_size(backing_fd));
if (fd==-1) {
perror(argv[1]);
return 1;
}

main_daemon(fd, backing_fd);
return 0;
}
Gå til top

Opret dig som bruger i dag

Det er gratis, og du binder dig ikke til noget.

Når du er oprettet som bruger, får du adgang til en lang række af sidens andre muligheder, såsom at udforme siden efter eget ønske og deltage i diskussionerne.

Opret Bruger Login