Bug Summary

File:medialib.c
Location:line 121, column 36
Description:Array access (via field 'playlists') results in a null pointer dereference

Annotated Source Code

1/*
2 * Copyright (c) 2010, 2011, 2012 Ryan Flannery <ryan.flannery@gmail.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "medialib.h"
18
19/* The global media library struct */
20medialib mdb;
21
22/*
23 * Load the global media library from disk. The location of the database file
24 * and the directory containing all of the playlists must be specified.
25 */
26void
27medialib_load(const char *db_file, const char *playlist_dir)
28{
29 playlist *p;
30 char **pfiles;
31 int npfiles;
32 int i;
33
34 /* copy file/directory names */
35 mdb.db_file = strdup(db_file);
36 mdb.playlist_dir = strdup(playlist_dir);
37 if (mdb.db_file == NULL((void *)0) || mdb.playlist_dir == NULL((void *)0))
38 err(1, "failed to strdup db file and playlist dir in medialib_init");
39
40 /* setup pseudo-playlists */
41 mdb.library = playlist_new();
42 mdb.library->filename = NULL((void *)0);
43 mdb.library->name = strdup("--LIBRARY--");
44
45 mdb.filter_results = playlist_new();
46 mdb.filter_results->filename = NULL((void *)0);
47 mdb.filter_results->name = strdup("--FILTER--");
48
49 if (mdb.library->name == NULL((void *)0) || mdb.filter_results->name == NULL((void *)0))
50 err(1, "failed to strdup pseudo-names in medialib_load");
51
52 /* load the actual database */
53 medialib_db_load(db_file);
54
55 /* setup initial record keeping for playlists */
56 mdb.nplaylists = 0;
57 mdb.playlists_capacity = 2;
58 mdb.playlists = calloc(2, sizeof(playlist*));
59 if (mdb.playlists == NULL((void *)0))
60 err(1, "medialib_load: failed to allocate initial playlists");
61
62 /* add library/filter pseudo-playlists */
63 medialib_playlist_add(mdb.library);
64 medialib_playlist_add(mdb.filter_results);
65
66 /* load the rest */
67 npfiles = retrieve_playlist_filenames(mdb.playlist_dir, &pfiles);
68 for (i = 0; i < npfiles; i++) {
69 p = playlist_load(pfiles[i], mdb.library->files, mdb.library->nfiles);
70 medialib_playlist_add(p);
71 free(pfiles[i]);
72 }
73
74 /* set all playlists as saved initially */
75 for (i = 0; i < mdb.nplaylists; i++)
76 mdb.playlists[i]->needs_saving = false0;
77
78 free(pfiles);
79}
80
81/* free() all memory associated with global media library */
82void
83medialib_destroy()
84{
85 int i;
86
87 /* free the database */
88 for (i = 0; i < mdb.library->nfiles; i++)
89 mi_free(mdb.library->files[i]);
90
91 /* free all the playlists */
92 for (i = 0; i < mdb.nplaylists; i++)
93 playlist_free(mdb.playlists[i]);
94
95 /* free all other allocated mdb members */
96 free(mdb.playlists);
97 free(mdb.db_file);
98 free(mdb.playlist_dir);
99
100 /* reset counters */
101 mdb.nplaylists = 0;
102 mdb.playlists_capacity = 0;
103}
104
105/* add a new playlist to the media library */
106void
107medialib_playlist_add(playlist *p)
108{
109 playlist **new_playlists;
110
111 /* check to see if we need to resize the array */
112 if (mdb.nplaylists == mdb.playlists_capacity) {
1
Taking true branch
113 mdb.playlists_capacity += MEDIALIB_PLAYLISTS_CHUNK_SIZE100;
114 int size = mdb.playlists_capacity * sizeof(playlist*);
115 if ((new_playlists = realloc(mdb.playlists, size)) == NULL((void *)0))
2
Value assigned to 'new_playlists'
3
Assuming pointer value is null
4
Taking true branch
116 err(1, "medialib_playlist_add: realloc failed");
117
118 mdb.playlists = new_playlists;
5
Null pointer value stored to 'mdb.playlists'
119 }
120
121 mdb.playlists[mdb.nplaylists++] = p;
6
Array access (via field 'playlists') results in a null pointer dereference
122}
123
124/*
125 * remove a playlist from the media library, and disk, given the playlist's
126 * index in the playlists array.
127 */
128void
129medialib_playlist_remove(int pindex)
130{
131 int i;
132
133 if (pindex < 0 || pindex >= mdb.nplaylists)
134 errx(1, "medialib_playlist_remove: index %d out of range", pindex);
135
136 playlist_delete(mdb.playlists[pindex]);
137
138 /* reorder */
139 for (i = pindex + 1; i < mdb.nplaylists; i++)
140 mdb.playlists[i - 1] = mdb.playlists[i];
141
142 mdb.nplaylists--;
143}
144
145/*
146 * create the vitunes directory, database file (initially empty, and
147 * playlists directory.
148 */
149void
150medialib_setup_files(const char *vitunes_dir, const char *db_file,
151 const char *playlist_dir)
152{
153 struct stat sb;
154
155 /* create vitunes directory */
156 if (mkdir(vitunes_dir, S_IRWXU0000700) == -1) {
157 if (errno(*__errno()) == EEXIST17)
158 warnx("vitunes directory '%s' already exists (OK)", vitunes_dir);
159 else
160 err(1, "unable to create vitunes directory '%s'", vitunes_dir);
161 } else
162 warnx("vitunes directory '%s' created", vitunes_dir);
163
164 /* create playlists directory */
165 if (mkdir(playlist_dir, S_IRWXU0000700) == -1) {
166 if (errno(*__errno()) == EEXIST17)
167 warnx("playlists directory '%s' already exists (OK)", playlist_dir);
168 else
169 err(1, "unable to create playlists directory '%s'", playlist_dir);
170 } else
171 warnx("playlists directory '%s' created", playlist_dir);
172
173 /* create database file */
174 if (stat(db_file, &sb) < 0) {
175 if (errno(*__errno()) == ENOENT2) {
176
177 int version[3] = {DB_VERSION_MAJOR2, DB_VERSION_MINOR1, DB_VERSION_OTHER0};
178 FILE *f;
179
180 /* open for writing */
181 if ((f = fopen(db_file, "w")) == NULL((void *)0))
182 err(1, "failed to create database file '%s'", db_file);
183
184 /* save header & version */
185 fwrite("vitunes", strlen("vitunes"), 1, f);
186 fwrite(version, sizeof(version), 1, f);
187
188 warnx("empty database at '%s' created", db_file);
189 fclose(f);
190 } else
191 err(1, "database file '%s' exists, but cannot access it", db_file);
192 } else
193 warnx("database file '%s' already exists (OK)", db_file);
194}
195
196/* used to sort media db by filenames. */
197static int mi_cmp_fn(const void *ai, const void *bi)
198{
199 const meta_info **a2 = (const meta_info **) ai;
200 const meta_info **b2 = (const meta_info **) bi;
201 const meta_info *a = (const meta_info *) *a2;
202 const meta_info *b = (const meta_info *) *b2;
203
204 return strcmp(a->filename, b->filename);
205}
206
207/* load the library database into the global media library */
208void
209medialib_db_load(const char *db_file)
210{
211 meta_info *mi;
212 FILE *fin;
213 char header[255] = { 0 };
214 int version[3];
215
216 if ((fin = fopen(db_file, "r")) == NULL((void *)0))
217 err(1, "Failed to open database file '%s'", db_file);
218
219 /* read and check header & version */
220 fread(header, strlen("vitunes"), 1, fin);
221 if (strncmp(header, "vitunes", strlen("vitunes")) != 0)
222 errx(1, "Database file '%s' NOT a vitunes database", db_file);
223
224 fread(version, sizeof(version), 1, fin);
225 if (version[0] != DB_VERSION_MAJOR2 || version[1] != DB_VERSION_MINOR1
226 || version[2] != DB_VERSION_OTHER0) {
227 printf("Loading vitunes database: old database version detected.\n");
228 printf("\tExisting database at '%s' is of version %d.%d.%d\n",
229 db_file, version[0], version[1], version[2]);
230 printf("\tThis version of vitunes only works with version %d.%d.%d\n",
231 DB_VERSION_MAJOR2, DB_VERSION_MINOR1, DB_VERSION_OTHER0);
232 printf("Remove the existing database and rebuild by doing:\n");
233 printf("\t$ rm %s\n", db_file);
234 printf("\t$ vitunes -e init\n");
235 printf("\t$ vitunes -e add path [...]\n");
236 fflush(stdout(&__sF[1]));
237 exit(1);
238 }
239
240 /* read rest of records */
241 while (!feof(fin)(!__isthreaded ? (((fin)->_flags & 0x0020) != 0) : (feof
)(fin))
) {
242 mi = mi_new();
243 mi_fread(mi, fin);
244 if (feof(fin)(!__isthreaded ? (((fin)->_flags & 0x0020) != 0) : (feof
)(fin))
)
245 mi_free(mi);
246 else if (ferror(fin)(!__isthreaded ? (((fin)->_flags & 0x0040) != 0) : (ferror
)(fin))
)
247 err(1, "Error loading database file '%s'", db_file);
248 else
249 playlist_files_append(mdb.library, &mi, 1, false0);
250 }
251
252 fclose(fin);
253
254 /* sort library by filenames */
255 qsort(mdb.library->files, mdb.library->nfiles, sizeof(meta_info*), mi_cmp_fn);
256}
257
258/* save the library database from the global media library to disk */
259void
260medialib_db_save(const char *db_file)
261{
262 FILE *fout;
263 int version[3] = {DB_VERSION_MAJOR2, DB_VERSION_MINOR1, DB_VERSION_OTHER0};
264 int i;
265
266 if ((fout = fopen(db_file, "w")) == NULL((void *)0))
267 err(1, "medialib_db_save: failed to open database file '%s'", db_file);
268
269 /* save header & version */
270 fwrite("vitunes", strlen("vitunes"), 1, fout);
271 fwrite(version, sizeof(version), 1, fout);
272
273 /* save records */
274 for (i = 0; i < mdb.library->nfiles; i++) {
275 mi_fwrite(mdb.library->files[i], fout);
276 if (ferror(fout)(!__isthreaded ? (((fout)->_flags & 0x0040) != 0) : (ferror
)(fout))
)
277 err(1, "medialib_db_save: error saving database");
278 }
279
280 fclose(fout);
281}
282
283/* flush the library to stdout in a csv format */
284void
285medialib_db_flush(FILE *fout, const char *timefmt)
286{
287 meta_info *mi;
288 struct tm *ltime;
289 char stime[255];
290 int f, i;
291 size_t len;
292
293 /* header row */
294 fprintf(fout, "filename, ");
295 for (i = 0; i < MI_NUM_CINFO8; i++)
296 fprintf(fout, "\"%s\", ", MI_CINFO_NAMES[i]);
297
298 fprintf(fout, "length-seconds, is_url, \"last-updated\"\n");
299 fflush(fout);
300
301 /* start output of db */
302 for (f = 0; f < mdb.library->nfiles; f++) {
303
304 /* get record */
305 mi = mdb.library->files[f];
306
307 /* output record */
308 fprintf(fout, "%s, ", mi->filename);
309 for (i = 0; i < MI_NUM_CINFO8; i++)
310 fprintf(fout, "\"%s\", ", mi->cinfo[i]);
311
312 /* convert last-updated time to string */
313 ltime = localtime(&(mi->last_updated));
314 len = strftime(stime, sizeof(stime), timefmt, ltime);
315 stime[len] = '\0';
316
317 fprintf(fout, "%i, %s, \"%s\"\n",
318 mi->length, (mi->is_url ? "true" : "false"), stime);
319
320 fflush(fout);
321 }
322}
323
324/*
325 * AFTER loading the global media library using medialib_load(), this function
326 * is used to re-scan all files that exist in the database and re-check their
327 * meta_info. Any files that no longer exist are removed, and any meta
328 * information that has changed is updated.
329 *
330 * The database is then re-saved to disk.
331 */
332void
333medialib_db_update(bool_Bool show_skipped, bool_Bool force_update)
334{
335 meta_info *mi;
336 struct stat sb;
337 char *filename;
338 int i;
339
340 /* stat counters */
341 int count_removed_file_gone = 0;
342 int count_removed_meta_gone = 0;
343 int count_skipped_not_updated = 0;
344 int count_updated = 0;
345 int count_errors = 0;
346 int count_urls = 0;
347
348 for (i = 0; i < mdb.library->nfiles; i++) {
349
350 filename = mdb.library->files[i]->filename;
351
352 /* skip url's */
353 if (mdb.library->files[i]->is_url) {
354 printf("s %s\n", filename);
355 count_urls++;
356 continue;
357 }
358
359 if (stat(filename, &sb) == -1) {
360
361 /* file was removed -or- stat() failed */
362
363 if (errno(*__errno()) == ENOENT2) {
364 /* file was removed, remove from library */
365 playlist_files_remove(mdb.library, i, 1, false0);
366 i--; /* since removed a file, we want to decrement i */
367 printf("x %s\n", filename);
368 count_removed_file_gone++;
369 } else {
370 /* stat() failed for some reason - unknown error */
371 printf("? %s\n", filename);
372 count_errors++;
373 }
374
375 } else {
376
377 /*
378 * file still exists... check if it has been modified since we
379 * last extracted meta-info from it (otherwise we ignore)
380 */
381
382 if (force_update ||
383 (sb.st_mtimest_mtim.tv_sec > mdb.library->files[i]->last_updated)) {
384
385 mi = mi_extract(filename);
386 if (mi == NULL((void *)0)) {
387 /* file now has no meta-info, remove from library */
388 playlist_files_remove(mdb.library, i, 1, false0);
389 i--; /* since removed a file, we want to decrement i */
390 printf("- %s\n", filename);
391 count_removed_meta_gone++;
392 } else {
393 /* file's meta-info has changed, update it */
394 mi_sanitize(mi);
395 playlist_file_replace(mdb.library, i, mi);
396 printf("u %s\n", filename);
397 count_updated++;
398 }
399 } else {
400 count_skipped_not_updated++;
401 if (show_skipped)
402 printf(". %s\n", filename);
403 }
404
405 }
406 }
407
408 /* save to file */
409 medialib_db_save(mdb.db_file);
410
411 /* output some of our stats */
412 printf("--------------------------------------------------\n");
413 printf("Results of updating database...\n");
414 printf("(s) %9d url's skipped\n", count_urls);
415 printf("(u) %9d files updated\n", count_updated);
416 printf("(x) %9d files removed (file no longer exists)\n",
417 count_removed_file_gone);
418 printf("(-) %9d files removed (meta-info gone)\n",
419 count_removed_meta_gone);
420 printf("(.) %9d files skipped (file unchanged since last checked)\n",
421 count_skipped_not_updated);
422 printf("(?) %9d files with errors (couldn't stat, but kept)\n",
423 count_errors);
424}
425
426/*
427 * AFTER loading the global media library using medialib_load(), this function
428 * will scan the list of directories specified in the parameter and
429 */
430void
431medialib_db_scan_dirs(char *dirlist[])
432{
433 FTS *fts;
434 FTSENT *ftsent;
435 meta_info *mi;
436 char fullname[PATH_MAX1024];
437 int i, idx;
438
439 /* stat counters */
440 int count_removed_lost_info = 0;
441 int count_updated = 0;
442 int count_skipped_no_info = 0;
443 int count_skipped_dir = 0;
444 int count_skipped_error = 0;
445 int count_skipped_not_updated = 0;
446 int count_added = 0;
447
448
449
450 fts = fts_open(dirlist, FTS_LOGICAL0x0002 | FTS_NOCHDIR0x0004, NULL((void *)0));
451 if (fts == NULL((void *)0))
452 err(1, "medialib_db_scan_dirs: fts_open failed");
453
454 while ((ftsent = fts_read(fts)) != NULL((void *)0)) {
455
456 switch (ftsent->fts_info) { /* file type */
457 case FTS_D1: /* TYPE: directory (going in) */
458 printf("Checking Directory: %s\n", ftsent->fts_path);
459 break;
460
461 case FTS_DP6: /* TYPE: directory (coming out) */
462 break;
463
464 case FTS_DNR4: /* TYPE: unreadable directory */
465 printf("Directory '%s' Unreadable\n", ftsent->fts_accpath);
466 count_skipped_dir++;
467 break;
468
469 case FTS_NS10: /* TYPE: file/dir that couldn't be stat(2) */
470 case FTS_ERR7: /* TYPE: other error */
471 printf("? %s\n", ftsent->fts_path);
472 count_skipped_error++;
473 break;
474
475 case FTS_F8: /* TYPE: regular file */
476
477 /* get the full name for the file */
478 if (realpath(ftsent->fts_accpath, fullname) == NULL((void *)0)) {
479 err(1, "medialib_db_scan_dirs: realpath failed for '%s'",
480 ftsent->fts_accpath);
481 }
482
483 /* check if the file already exists in the db */
484 idx = -1;
485 for (i = 0; i < mdb.library->nfiles; i++) {
486 if (strcmp(fullname, mdb.library->files[i]->filename) == 0)
487 idx = i;
488 }
489
490 if (idx != -1) {
491 /* file already exists in library database - update */
492
493 if (ftsent->fts_statp->st_mtimest_mtim.tv_sec >
494 mdb.library->files[idx]->last_updated) {
495
496 /* file has been modified since we last extracted info */
497
498 mi = mi_extract(ftsent->fts_accpath);
499
500 if (mi == NULL((void *)0)) {
501 /* file now has no meta-info, remove from library */
502 playlist_files_remove(mdb.library, idx, 1, false0);
503 printf("- %s\n", ftsent->fts_accpath);
504 count_removed_lost_info++;
505 } else {
506 /* file's meta-info has changed, update it */
507 mi_sanitize(mi);
508 playlist_file_replace(mdb.library, idx, mi);
509 printf("u %s\n", ftsent->fts_accpath);
510 count_updated++;
511 }
512 } else {
513 printf(". %s\n", ftsent->fts_accpath);
514 count_skipped_not_updated++;
515 }
516
517 } else {
518
519 /* file does NOT exists in library database - add it */
520
521 mi = mi_extract(ftsent->fts_accpath);
522
523 if (mi == NULL((void *)0)) {
524 /* file has no info */
525 printf("s %s\n", ftsent->fts_accpath);
526 count_skipped_no_info++;
527 } else {
528 /* file does have info, add it to library */
529 mi_sanitize(mi);
530 playlist_files_append(mdb.library, &mi, 1, false0);
531 printf("+ %s\n", ftsent->fts_accpath);
532 count_added++;
533 }
534 }
535 }
536 }
537
538 if (fts_close(fts) == -1)
539 err(1, "medialib_db_scan_dirs: failed to close file heirarchy");
540
541 /* save to file */
542 medialib_db_save(mdb.db_file);
543
544 /* output some of our stats */
545 printf("--------------------------------------------------\n");
546 printf("Results of scanning directories...\n");
547 printf("(+) %9d files added\n", count_added);
548 printf("(u) %9d files updated\n", count_updated);
549 printf("(-) %9d files removed (was in DB, but no longer has meta-info)\n",
550 count_removed_lost_info);
551 printf("(.) %9d files skipped (in DB, file unchanged since last checked)\n",
552 count_skipped_not_updated);
553 printf("(s) %9d files skipped (no info)\n", count_skipped_no_info);
554 printf("(?) %9d files skipped (other error)\n", count_skipped_error);
555 printf(" %9d directories skipped (couldn't read)\n", count_skipped_dir);
556}