summaryrefslogtreecommitdiffabout
path: root/libid3tag
authorSergey Poznyakoff <gray@gnu.org.ua>2009-02-09 19:07:48 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2009-02-09 19:07:48 (GMT)
commitf6c2e714cc84ad327cf661967cdb024f835dd1c1 (patch) (side-by-side diff)
tree00290bad3ee72eb52c614ddbb8c97eb7389996aa /libid3tag
parent06af0596299d003ccb7651f45a99f8b8a28a6412 (diff)
downloadidest-f6c2e714cc84ad327cf661967cdb024f835dd1c1.tar.gz
idest-f6c2e714cc84ad327cf661967cdb024f835dd1c1.tar.bz2
Bugfix in libid3tag.
* libid3tag/file.c (v2_write): Implement general case.
Diffstat (limited to 'libid3tag') (more/less context) (ignore whitespace changes)
-rw-r--r--libid3tag/file.c149
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;
}
/*

Return to:

Send suggestions and report system problems to the System administrator.