ref: efbec63da9c094e0908047352d1d5480dfbc4b97
dir: /part.c/
#include <ext4.h>
#include <thread.h>
#include "group.h"
#include "common.h"
#define TRACE(fmt, ...) //fprint(2, fmt, __VA_ARGS__)
#define BDEV2PART(bdev) ((bdev)->bdif->p_user)
static struct {
QLock;
Part *ps;
u32int id;
}sv;
static long
preadn(int f, void *av, long n, vlong offset)
{
char *a;
long m, t;
assert(offset >= 0);
a = av;
t = 0;
while(t < n){
m = pread(f, a+t, n-t, offset);
if(m <= 0){
if(t == 0)
return m;
break;
}
t += m;
offset += m;
}
return t;
}
static int
bdopen(struct ext4_blockdev *bdev)
{
Part *p;
p = BDEV2PART(bdev);
TRACE("bdopen %p\n", p);
USED(p);
return 0;
}
static int
bdread(struct ext4_blockdev *bdev, void *buf, u64int blkid, u32int blkcnt)
{
Part *p;
p = BDEV2PART(bdev);
TRACE("bdread %p %p %llud %ud\n", p, buf, blkid, blkcnt);
if(preadn(p->f, buf, blkcnt*p->bdif.ph_bsize, blkid*p->bdif.ph_bsize) != blkcnt*p->bdif.ph_bsize){
fprint(2, "bdread: %r\n");
return EIO;
}
return 0;
}
static int
bdwrite(struct ext4_blockdev *bdev, const void *buf, u64int blkid, u32int blkcnt)
{
Part *p;
p = BDEV2PART(bdev);
TRACE("bdwrite %p %p %llud %ud\n", p, buf, blkid, blkcnt);
if(pwrite(p->f, buf, blkcnt*p->bdif.ph_bsize, blkid*p->bdif.ph_bsize) != blkcnt*p->bdif.ph_bsize)
return EIO;
return 0;
}
static int
bdclose(struct ext4_blockdev *bdev)
{
Part *p;
p = BDEV2PART(bdev);
TRACE("bdclose %p\n", p);
USED(p);
return 0;
}
static int
bdlock(struct ext4_blockdev *bdev)
{
Part *p;
p = BDEV2PART(bdev);
TRACE("bdlock %p\n", p);
qlock(p);
return 0;
}
static int
bdunlock(struct ext4_blockdev *bdev)
{
Part *p;
p = BDEV2PART(bdev);
TRACE("bdunlock %p\n", p);
qunlock(p);
return 0;
}
static int
getblksz(char *dev, u32int *blksz)
{
char *s, *e, *g, *a[5];
vlong x;
int f, n, r;
/* default blksz if couldn't find out the real one */
*blksz = 512;
f = -1;
g = nil;
if((s = smprint("%s_ctl", dev)) == nil)
goto error;
cleanname(s);
if((e = strrchr(s, '/')) == nil)
e = s;
strcpy(e, "/ctl");
f = open(s, OREAD);
free(s);
if(f >= 0){
if((g = malloc(4096)) == nil)
goto error;
for(n = 0; (r = read(f, g+n, 4096-n-1)) > 0; n += r);
g[n] = 0;
close(f);
f = -1;
for(s = g; (e = strchr(s, '\n')) != nil; s = e+1){
*e = 0;
if(tokenize(s, a, nelem(a)) >= 3 && strcmp(a[0], "geometry") == 0){
if((x = strtoll(a[2], &e, 0)) > 0 && *e == 0)
*blksz = x;
if(*blksz != x){
werrstr("invalid block size: %s", a[2]);
goto error;
}
break;
}
}
}
close(f);
free(g);
return 0;
error:
close(f);
free(g);
return -1;
}
static int
fmtpart(Fmt *f)
{
Part *p;
p = va_arg(f->args, Part*);
return fmtprint(f, f->r == 'M' ? "/%#llux" : "dev%#llux", p->qid.path);
}
static void *
readfile(Part *p, char *path, uvlong *sz)
{
char *s, *d;
ext4_file f;
uvlong n;
ulong got;
int r;
d = nil;
while(*path == '/')
path++;
s = smprint("%M/%s", p, path);
r = ext4_fopen2(&f, s, O_RDONLY);
free(s);
if(r == 0){
*sz = ext4_fsize(&f);
if((d = malloc(*sz+1)) == nil){
ext4_fclose(&f);
goto error;
}
for(n = 0; n < *sz; n += got){
if((r = ext4_fread(&f, d+n, *sz-n, &got)) != 0){
werrstr("readfile: %s", errno2s(r));
ext4_fclose(&f);
goto error;
}
if(got == 0)
break;
}
*sz = n;
ext4_fclose(&f);
}else{
error:
free(d);
d = nil;
*sz = 0;
}
return d;
}
static int
mountpart(Part *p, Opts *opts)
{
int r;
char *gr;
uvlong sz;
if(snprint(p->dev, sizeof(p->dev), "%Ð", p) >= sizeof(p->dev)){
werrstr("part path too long");
goto error;
}
if(snprint(p->mnt, sizeof(p->mnt), "%M/", p) >= sizeof(p->mnt)){
werrstr("part path too long");
goto error;
}
if((r = ext4_device_register(&p->bdev, p->dev)) != 0){
werrstr("register: %s", errno2s(r));
goto error;
}
if((r = ext4_mount(p->dev, p->mnt, false)) != 0){
werrstr("mount: %s", errno2s(r));
goto error;
}
if((r = ext4_recover(p->mnt)) != 0 && r != ENOTSUP){
werrstr("recover: %s", errno2s(r));
goto error;
}
if((r = ext4_journal_start(p->mnt)) != 0 && r != ENOTSUP){
werrstr("journal: %s", errno2s(r));
goto error;
}
if(opts->cachewb)
ext4_cache_write_back(p->mnt, 1);
if((r = ext4_get_sblock(p->mnt, &p->sb)) != 0){
werrstr("sblock: %s", errno2s(r));
goto error;
}
if((gr = readfile(p, "/etc/group", &sz)) != nil){
gr[sz] = 0;
r = loadgroups(&p->groups, gr);
free(gr);
if(r != 0)
goto error;
}
return 0;
error:
werrstr("mountpart: %r");
return -1;
}
Part *
openpart(char *dev, Opts *opts)
{
Dir *d;
Part *p;
char *s;
int f;
u32int blksz;
d = nil;
p = nil;
s = nil;
qlock(&sv);
fmtinstall(L'Ð', fmtpart);
fmtinstall('M', fmtpart);
f = open(dev, ORDWR);
if(f < 0 || (d = dirfstat(f)) == nil)
goto error;
/* see if it's already opened */
for(p = sv.ps; p != nil && p->qid.path != d->qid.path; p = p->next);
if(p == nil){ /* no? then make one */
if(getblksz(dev, &blksz) != 0 || (p = calloc(1, sizeof(*p)+blksz)) == nil)
goto error;
p->f = f;
p->qid = d->qid;
p->bdev.bdif = &p->bdif;
p->bdev.part_size = d->length;
p->bdif.open = bdopen;
p->bdif.bread = bdread;
p->bdif.bwrite = bdwrite;
p->bdif.close = bdclose;
p->bdif.lock = bdlock;
p->bdif.unlock = bdunlock;
p->bdif.ph_bsize = blksz;
p->bdif.ph_bcnt = d->length/blksz;
p->bdif.ph_bbuf = p->blkbuf;
p->bdif.p_user = p;
if(mountpart(p, opts) != 0)
goto error;
p->next = sv.ps;
if(sv.ps != nil)
sv.ps->prev = p;
sv.ps = p;
p->qidmask.path = ((uvlong)sv.id++) << 32;
p->qidmask.type = QTDIR;
}else{
close(f);
}
free(d);
free(s);
qunlock(&sv);
return p;
error:
werrstr("openpart: %r");
if(f >= 0)
close(f);
free(d);
free(p);
free(s);
qunlock(&sv);
return nil;
}
void
closepart(Part *p)
{
int r;
qlock(&sv);
ext4_cache_write_back(p->mnt, 0);
if((r = ext4_journal_stop(p->mnt)) != 0 && r != ENOTSUP)
fprint(2, "closepart: journal %s: %s\n", p->mnt, errno2s(r));
if((r = ext4_umount(p->mnt)) != 0 && r != ENOTSUP)
fprint(2, "closepart: umount %s: %s\n", p->mnt, errno2s(r));
if((r = ext4_device_unregister(p->dev)) != 0 && r != ENOTSUP)
fprint(2, "closepart: unregister %s: %s\n", p->dev, errno2s(r));
close(p->f);
if(p->prev != nil)
p->prev = p->next;
if(p->next != nil)
p->next->prev = p->prev;
if(p == sv.ps)
sv.ps = p->next;
freegroups(&p->groups);
free(p);
qunlock(&sv);
}
void
closeallparts(void)
{
while(sv.ps != nil)
closepart(sv.ps);
}