]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - aft.c
Remove ->fd of struct audio file data.
[paraslash.git] / aft.c
diff --git a/aft.c b/aft.c
index c04d4f9c99e89afb451afe94be7fcc64461b15e1..c8c98e7ab679b5a5254eed9405afa3cca6cf870e 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -407,7 +407,7 @@ static void load_chunk_table(struct afh_info *afhi, const struct osl_object *ct)
        int i;
        size_t sz;
 
-       if (!ct->data || ct->size < 4) {
+       if (!ct->data || ct->size < 4 * (afhi->chunks_total + 1)) {
                afhi->chunk_table = NULL;
                return;
        }
@@ -589,8 +589,10 @@ static int get_hash_of_row(const struct osl_row *row, unsigned char **hash)
 int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi)
 {
        struct osl_object obj;
-       int ret = osl(osl_get_object(audio_file_table, row, AFTCOL_AFHI,
-               &obj));
+       int ret;
+
+       assert(row);
+       ret = osl(osl_get_object(audio_file_table, row, AFTCOL_AFHI, &obj));
        if (ret < 0)
                return ret;
        load_afhi(obj.data, afhi);
@@ -719,7 +721,8 @@ __a_const static short unsigned get_duration_width(int seconds)
        return width + 6;
 }
 
-static void get_duration_buf(int seconds, char *buf, struct ls_options *opts)
+static void get_duration_buf(int seconds, char *buf, size_t bufsize,
+               struct ls_options *opts)
 {
        unsigned hours = seconds / 3600, mins = (seconds % 3600) / 60;
        short unsigned max_width;
@@ -727,10 +730,12 @@ static void get_duration_buf(int seconds, char *buf, struct ls_options *opts)
        if (!hours) { /* m:ss or mm:ss */
                max_width = opts->mode == LS_MODE_LONG?
                        opts->widths.duration_width : 4;
+               assert(max_width < bufsize - 1);
                sprintf(buf, "%*u:%02d", max_width - 3, mins, seconds % 60);
        } else { /* more than one hour => h:mm:ss, hh:mm:ss, hhh:mm:ss, ... */
                max_width = opts->mode == LS_MODE_LONG?
                        opts->widths.duration_width : 7;
+               assert(max_width < bufsize - 1);
                sprintf(buf, "%*u:%02u:%02d", max_width - 6, hours, mins,
                        seconds % 60);
        }
@@ -854,7 +859,8 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                if (ret < 0)
                        goto out;
        }
-       get_duration_buf(afhi->seconds_total, duration_buf, opts);
+       get_duration_buf(afhi->seconds_total, duration_buf,
+               sizeof(duration_buf), opts);
        if (opts->mode == LS_MODE_LONG) {
                struct ls_widths *w = &opts->widths;
                if (lls_opt_given(r_a))
@@ -1018,7 +1024,14 @@ out:
 /**
  * Open the audio file with highest score and set up an afd structure.
  *
- * \param afd Result pointer.
+ * This determines and opens the next audio file, verifies that it did not
+ * change by comparing the recomputed the hash value of the file contents
+ * against the value stored in the audio file table. If all goes well, it
+ * creates a shared memory area containing the serialized version of the afd
+ * structure, including the chunk table, if any. The caller can then send the
+ * ID of this area and the open fd to the server process.
+ *
+ * \param fd Result pointer for the file descriptor of the audio file.
  *
  * On success, the numplayed field of the audio file selector info is increased
  * and the lastplayed time is set to the current time. Finally, the score of
@@ -1026,7 +1039,7 @@ out:
  *
  * \return Positive shmid on success, negative on errors.
  */
-int open_and_update_audio_file(struct audio_file_data *afd)
+int open_and_update_audio_file(int *fd)
 {
        unsigned char file_hash[HASH_SIZE];
        struct osl_object afsi_obj;
@@ -1035,28 +1048,45 @@ int open_and_update_audio_file(struct audio_file_data *afd)
        struct afsi_change_event_data aced;
        struct osl_object map, chunk_table_obj;
        struct ls_data *d = &status_item_ls_data;
+       unsigned char *tmp_hash;
+       struct audio_file_data afd;
 again:
        ret = score_get_best(&current_aft_row, &d->score);
        if (ret < 0)
                return ret;
-       ret = get_hash_of_row(current_aft_row, &d->hash);
+       /*
+        * get_hash_of_row() and get_audio_file_path_of_row() initialize
+        * their pointer argument to point to memory-mapped files. These pointers
+        * become stale after a new audio file has been added or after the
+        * server process received SIGHUP. For in both cases libosl unmaps and
+        * remaps the underlying database files, and this remapping may well
+        * change the starting address of the mapping. To avoid stale pointer
+        * references we create copies on the heap.
+        */
+       ret = get_hash_of_row(current_aft_row, &tmp_hash);
        if (ret < 0)
                return ret;
+       if (!d->hash)
+               d->hash = para_malloc(HASH_SIZE);
+       memcpy(d->hash, tmp_hash, HASH_SIZE);
+       free(d->path);
        ret = get_audio_file_path_of_row(current_aft_row, &d->path);
        if (ret < 0)
                return ret;
        PARA_NOTICE_LOG("%s\n", d->path);
+       d->path = para_strdup(d->path);
+
        ret = get_afsi_object_of_row(current_aft_row, &afsi_obj);
        if (ret < 0)
                return ret;
        ret = load_afsi(&d->afsi, &afsi_obj);
        if (ret < 0)
                return ret;
-       ret = get_afhi_of_row(current_aft_row, &afd->afhi);
+       ret = get_afhi_of_row(current_aft_row, &afd.afhi);
        if (ret < 0)
                return ret;
-       d->afhi = afd->afhi;
-       d->afhi.chunk_table = afd->afhi.chunk_table = NULL;
+       d->afhi = afd.afhi;
+       d->afhi.chunk_table = afd.afhi.chunk_table = NULL;
        ret = osl(osl_open_disk_object(audio_file_table, current_aft_row,
                AFTCOL_CHUNKS, &chunk_table_obj));
        if (ret < 0) {
@@ -1068,7 +1098,7 @@ again:
        } else {
                PARA_INFO_LOG("chunk table: %zu bytes\n", chunk_table_obj.size);
        }
-       ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd);
+       ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, fd);
        if (ret < 0)
                goto out;
        hash_function(map.data, map.size, file_hash);
@@ -1083,8 +1113,8 @@ again:
        new_afsi.last_played = time(NULL);
        save_afsi(&new_afsi, &afsi_obj); /* in-place update */
 
-       afd->audio_format_id = d->afsi.audio_format_id;
-       load_chunk_table(&afd->afhi, &chunk_table_obj);
+       afd.audio_format_id = d->afsi.audio_format_id;
+       load_chunk_table(&afd.afhi, &chunk_table_obj);
        aced.aft_row = current_aft_row;
        aced.old_afsi = &d->afsi;
        /*
@@ -1094,9 +1124,9 @@ again:
        ret = afs_event(AFSI_CHANGE, NULL, &aced);
        if (ret < 0)
                goto out;
-       ret = save_afd(afd);
+       ret = save_afd(&afd);
 out:
-       free(afd->afhi.chunk_table);
+       free(afd.afhi.chunk_table);
        if (chunk_table_obj.data)
                osl_close_disk_object(&chunk_table_obj);
        if (ret < 0) {
@@ -2518,10 +2548,12 @@ static int aft_open(const char *dir)
                PARA_NOTICE_LOG("current audio file hash lookup: success\n");
                return 1;
        }
-       PARA_NOTICE_LOG("failed to open audio file table\n");
        audio_file_table = NULL;
-       if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
+       if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT)) {
+               PARA_WARNING_LOG("no audio file table\n");
                return 1;
+       }
+       PARA_NOTICE_LOG("failed to open audio file table\n");
        return ret;
 }
 
@@ -2579,7 +2611,8 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
                ret = get_audio_file_path_of_row(current_aft_row, &path);
                if (ret < 0)
                        return ret;
-               status_item_ls_data.path = path;
+               free(status_item_ls_data.path);
+               status_item_ls_data.path = para_strdup(path);
                make_status_items();
                return 1;
        } case AFHI_CHANGE: {
@@ -2601,8 +2634,14 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
                /*
                 * These events are rare. We don't bother to check whether the
                 * current status items are affected and simply recreate them
-                * every time.
+                * whenever an audio file is open.
                 */
+               if (!current_aft_row)
+                       return 0;
+               ret = get_afhi_of_row(current_aft_row,
+                       &status_item_ls_data.afhi);
+               if (ret < 0)
+                       return ret;
                make_status_items();
                return 0;
        } default: