Кода стана твърде дълъг, все пак ще се опитам да го постна. Използва libid3tag и libspfs от проекта npfs на sourceforge (писан от колега-булгар).
Програмата се пуска:
mp3fs srcdir
Остава в background и чака на порт 1234 (може да се промени с опция -p). Файловата система се монтира:
mount -t 9p 127.0.0.1 /mnt/mp3 -o port=1234
GeSHi (C):
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <dirent.h>
#include <id3tag.h>
#include <spfs.h>
Spfile *createfile(Spfile *parent, char *name, u32 mode, void *ops, void *aux);
Spfile *findfile(Spfile *parent, char *name);
Spfile *dirfirst(Spfile *dir);
Spfile *dirnext(Spfile *dir, Spfile *prevchild);
int fileread(Spfilefid *, u64, u32, u8 *, Spreq *);
int fileopenfid(Spfilefid *);
void fileclosefid(Spfilefid *);
Spdirops dops = {
.first = dirfirst,
.next = dirnext,
};
Spfileops fops = {
.read = fileread,
.openfid = fileopenfid,
.closefid = fileclosefid,
};
Spsrv *srv;
Spfile *root;
Spuser *user;
int nextid;
void
sysfatal(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(1);
}
char *
getid(struct id3_tag *tag, char *id)
{
struct id3_frame *frm;
id3_ucs4_t const *str;
char *ret;
frm = id3_tag_findframe(tag, id, 0);
if (!frm)
return NULL;
if (strcmp(id, ID3_FRAME_COMMENT) == 0)
str = id3_field_getfullstring(&frm->fields[3]);
else
str = id3_field_getstrings(&frm->fields[1], 0);
if (!str)
return NULL;
ret = id3_ucs4_utf8duplicate(str);
return ret;
}
char *
gettitle(struct id3_tag *tag)
{
return getid(tag, ID3_FRAME_TITLE);
}
char *
getartist(struct id3_tag *tag)
{
return getid(tag, ID3_FRAME_ARTIST);
}
char *
getalbum(struct id3_tag *tag)
{
return getid(tag, ID3_FRAME_ALBUM);
}
int
process(char *fname)
{
char *fn, *artist, *album, *title;
struct stat st;
DIR *d;
struct dirent *de;
struct id3_file *f;
struct id3_tag *t;
Spfile *artdir, *albdir;
if (stat(fname, &st) < 0)
sysfatal("cannot stat %s", fname);
if (S_ISDIR(st.st_mode)) {
d = opendir(fname);
if (!d)
sysfatal("cannot open directory %s", fname);
while ((de = readdir(d)) != NULL) {
if (de->d_name[0] == '.' && (de->d_name[1] == '.' ||
de->d_name[1] == '\0'))
continue;
fn = malloc(strlen(fname) + strlen(de->d_name) + 2);
sprintf(fn, "%s/%s", fname, de->d_name);
process(fn);
free(fn);
}
closedir(d);
}
if (!S_ISREG(st.st_mode))
return 0;
f = id3_file_open(fname, ID3_FILE_MODE_READONLY);
if (!f)
sysfatal("cannot open file %s", fname);
t = id3_file_tag(f);
if (!t)
sysfatal("cannot get tag");
artist = getartist(t);
album = getalbum(t);
title = gettitle(t);
if (!artist)
artist = strdup("Unknown Artist");
artdir = findfile(root, artist);
if (!artdir)
artdir = createfile(root, artist, 0555 | Dmdir, &dops, NULL);
if (!album)
album = strdup("Unknown Album");
albdir = findfile(artdir, album);
if (!albdir)
albdir = createfile(artdir, album, 0555 | Dmdir, &dops, NULL);
if (!title) {
title = malloc(64);
snprintf(title, 64, "Track%d", nextid);
}
fprintf(stderr, "process %s\n", fname);
createfile(albdir, title, 0444, &fops, strdup(fname));
id3_file_close(f);
return 0;
}
Spfile *
findfile(Spfile *parent, char *name)
{
Spfile *f;
for(f = parent->dirfirst; f != NULL; f = f->next)
if (strcmp(f->name, name) == 0)
return f;
return NULL;
}
Spfile *
createfile(Spfile *parent, char *name, u32 mode, void *ops, void *aux)
{
Spfile *ret;
ret = spfile_alloc(parent, name, mode, nextid++, ops, aux);
if (parent->dirlast) {
parent->dirlast->next = ret;
ret->prev = parent->dirlast;
} else
parent->dirfirst = ret;
parent->dirlast = ret;
ret->atime = ret->mtime = time(NULL);
ret->uid = ret->muid = user;
ret->gid = user->dfltgroup;
spfile_incref(ret);
return ret;
}
Spfile*
dirfirst(Spfile *dir)
{
spfile_incref(dir->dirfirst);
return dir->dirfirst;
}
Spfile*
dirnext(Spfile *dir, Spfile *prevchild)
{
spfile_incref(prevchild->next);
return prevchild->next;
}
int
fileread(Spfilefid *fid, u64 offset, u32 count, u8 *data, Spreq *req)
{
int n, fd;
fd = (int) fid->aux;
n = pread(fd, data, count, offset);
if (n < 0)
sp_uerror(errno);
return n;
}
int
fileopenfid(Spfilefid *fid)
{
int fd;
char *fname;
Spfile *f;
f = fid->file;
fname = (char *) f->aux;
fd = open(fname, O_RDONLY);
if (fd < 0) {
sp_uerror(errno);
return 0;
}
fid->aux = (void *) fd;
return 1;
}
void
fileclosefid(Spfilefid *fid)
{
int fd;
fd = (int) fid->aux;
close(fd);
}
void
usage(void)
{
fprintf(stderr, "mp3fs -d -p port srcdir\n");
exit(1);
}
int
main(int argc, char *argv[])
{
int port, ecode, c, debuglevel;
pid_t pid;
char *s, *ename;
user = sp_uid2user(geteuid());
root = spfile_alloc(NULL, "", 0555 | Dmdir, 0, &dops, NULL);
root->parent = root;
spfile_incref(root);
root->atime = root->mtime = time(NULL);
root->uid = root->muid = user;
root->gid = user->dfltgroup;
port = 1234;
while ((c = getopt(argc, argv, "dp:")) != -1) {
switch (c) {
case 'd':
debuglevel = 1;
break;
case 'p':
port = strtol(optarg, &s, 10);
if (*s != '\0')
usage();
break;
case 'h':
default:
usage();
}
}
if (optind >= argc)
usage();
process(argv[optind]);
srv = sp_socksrv_create_tcp(&port);
if (!srv)
goto error;
srv->debuglevel = debuglevel;
spfile_init_srv(srv, root);
sp_srv_start(srv);
if (!debuglevel) {
close(0);
open("/dev/null", O_RDONLY);
close(1);
open("/dev/null", O_WRONLY);
close(2);
open("/dev/null", O_WRONLY);
pid = fork();
if (pid < 0) {
fprintf(stderr, "cannot fork\n");
return -1;
}
if (pid != 0) {
/* parent */
return 0;
}
/* child */
setsid();
chdir("/");
}
fprintf(stderr, "start listening\n");
sp_poll_loop();
return 0;
error:
sp_rerror(&ename, &ecode);
fprintf(stderr, "Error: %s\n", ename);
return -1;
}