diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-09 21:07:48 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-09 21:07:48 +0200 |
commit | f6c2e714cc84ad327cf661967cdb024f835dd1c1 (patch) | |
tree | 00290bad3ee72eb52c614ddbb8c97eb7389996aa /libid3tag | |
parent | 06af0596299d003ccb7651f45a99f8b8a28a6412 (diff) | |
download | idest-f6c2e714cc84ad327cf661967cdb024f835dd1c1.tar.gz idest-f6c2e714cc84ad327cf661967cdb024f835dd1c1.tar.bz2 |
Bugfix in libid3tag.
* libid3tag/file.c (v2_write): Implement general case.
Diffstat (limited to 'libid3tag')
-rw-r--r-- | libid3tag/file.c | 149 |
1 files changed, 141 insertions, 8 deletions
diff --git a/libid3tag/file.c b/libid3tag/file.c index 9d7319a..1ba2931 100644 --- a/libid3tag/file.c +++ b/libid3tag/file.c @@ -28,6 +28,9 @@ # include <stdio.h> # include <stdlib.h> # include <string.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <errno.h> # ifdef HAVE_UNISTD_H # include <unistd.h> @@ -567,6 +570,105 @@ int v1_write(struct id3_file *file, return 0; } +static char * +alloc_buf(size_t *pbufsize) +{ + char *buf = NULL; + size_t bufsize; + + for (bufsize = *pbufsize; + bufsize > 0 && (buf = malloc(bufsize)) == NULL; + bufsize /= 2); + *pbufsize = bufsize; + return buf; +} + +#define SIZE_T_MAX ((size_t)~0) + + +#define TNAME "ID3.XXXXXX" + +static FILE * +make_temp_file(struct id3_file *file, char **pname) +{ + char *template; + char *p; + int save_mask; + int fd; + int ec; + FILE *fp; + + p = strrchr(file->path, '/'); + if (p) { + size_t len = p - file->path + 1; + template = malloc(len + sizeof(TNAME) - 1); + if (!template) + return NULL; + memcpy(template, file->path, len); + strcpy(template + len, TNAME); + } else { + template = strdup(TNAME); + if (!template) + return NULL; + } + + save_mask = umask(077); + fd = mkstemp(template); + ec = errno; + umask(save_mask); + + if (fd == -1) { + errno = ec; + return NULL; + } + + fp = fdopen(fd, "w"); + if (!fp) { + ec = errno; + close(fd); + unlink(template); + free(template); + errno = ec; + } else + *pname = template; + return fp; +} + +static int +copy_block(FILE *inf, FILE *outf, off_t start, off_t size) +{ + char *buf; + size_t bufsize; + + if (fseek(inf, start, SEEK_SET) < 0) + return -1; + if (size == 0) + return 0; + + if (size < 0 || size > SIZE_T_MAX) + bufsize = SIZE_T_MAX; + else + bufsize = size; + + buf = alloc_buf(&bufsize); + while (1) { + size_t rdbytes; + + rdbytes = fread(buf, 1, bufsize, inf); + if (rdbytes == 0) + break; + if (fwrite(buf, rdbytes, 1, outf) != 1) + break; + if (size > 0) { + size -= rdbytes; + if (size == 0) + break; + } + } + free(buf); + return size > 0 ? 1 : 0; +} + /* * NAME: v2_write() * DESCRIPTION: write ID3v2 tag modifications to a file @@ -575,6 +677,8 @@ static int v2_write(struct id3_file *file, id3_byte_t const *data, id3_length_t length) { + int rc; + assert(!data || length > 0); if (data && @@ -586,17 +690,46 @@ int v2_write(struct id3_file *file, if (fseek(file->iofile, file->tags[0].location, SEEK_SET) == -1 || fwrite(data, length, 1, file->iofile) != 1 || fflush(file->iofile) == EOF) + rc = -1; + else + rc = 0; + } else { + /* hard general case: rewrite entire file */ + char *tmpname; + FILE *tmp; + struct stat st; + + if (stat(file->path, &st)) + return -1; + + tmp = make_temp_file(file, &tmpname); + if (!tmp) return -1; - goto done; + rc = copy_block(file->iofile, tmp, 0, file->tags[0].location); + if (rc == 0) { + rc = fwrite(data, length, 1, tmp) != 1; + if (rc == 0) + rc = copy_block(file->iofile, tmp, + file->tags[0].location + file->tags[0].length, -1); + } + fclose(tmp); + if (rc) + unlink(tmpname); + else { + file->tags[0].length = length; + fclose(file->iofile); + rc = unlink(file->path); + if (rc == 0) { + rc = rename(tmpname, file->path); + chmod(file->path, st.st_mode & 0777); + chown(file->path, st.st_uid, st.st_gid); + file->iofile = fopen(file->path, "r+b"); + } + } + free(tmpname); } - - /* hard general case: rewrite entire file */ - - /* ... */ - - done: - return 0; + return rc; } /* |