* Fix race condition with file-serving SQL queries and scanner/inotify SQL queries.

This commit is contained in:
Justin Maggard 2009-03-22 22:21:44 +00:00
parent c4d180f80d
commit 575556ca9d
3 changed files with 107 additions and 73 deletions

View File

@ -616,14 +616,13 @@ main(int argc, char * * argv)
}
if( sqlite3_open(DB_PATH "/files.db", &db) != SQLITE_OK )
{
fprintf(stderr, "ERROR: Failed to open sqlite database! Exiting...\n");
exit(-1);
DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to open sqlite database! Exiting...\n");
}
else
{
char **result;
int rows;
sqlite3_busy_timeout(db, 2500);
sqlite3_busy_timeout(db, 5000);
if( sql_get_table(db, "pragma user_version", &result, &rows, 0) == SQLITE_OK )
{
if( atoi(result[1]) != DB_VERSION ) {
@ -632,23 +631,21 @@ main(int argc, char * * argv)
unlink(DB_PATH "/files.db");
system("rm -rf " DB_PATH "/art_cache");
sqlite3_open(DB_PATH "/files.db", &db);
sqlite3_busy_timeout(db, 5000);
if( CreateDatabase() != 0 )
{
fprintf(stderr, "Error creating database!\n");
return -1;
DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqlite database! Exiting...\n");
}
if( pthread_create(&thread[0], NULL, start_scanner, NULL) )
{
printf("ERROR: pthread_create() failed\n");
exit(-1);
DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_scanner.\n");
}
}
sqlite3_free_table(result);
}
if( GETFLAG(INOTIFYMASK) && pthread_create(&thread[1], NULL, start_inotify, NULL) )
{
printf("ERROR: pthread_create() failed\n");
exit(-1);
DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify.\n");
}
}

9
sql.c
View File

@ -17,18 +17,19 @@
*/
#include <stdio.h>
#include "sql.h"
#include "log.h"
int
sql_exec(sqlite3 * db, const char * sql)
{
int ret;
char *errMsg = NULL;
//DEBUG printf("SQL: %s\n", sql);
//DPRINTF(E_DEBUG, L_DB_SQL, "SQL: %s\n", sql);
ret = sqlite3_exec(db, sql, 0, 0, &errMsg);
if( ret != SQLITE_OK )
{
fprintf(stderr, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
DPRINTF(E_ERROR, L_DB_SQL, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
if (errMsg)
sqlite3_free(errMsg);
}
@ -40,12 +41,12 @@ sql_get_table(sqlite3 *db, const char *sql, char ***pazResult, int *pnRow, int *
{
int ret;
char *errMsg = NULL;
//DEBUG printf("SQL: %s\n", sql);
//DPRINTF(E_DEBUG, L_DB_SQL, "SQL: %s\n", sql);
ret = sqlite3_get_table(db, sql, pazResult, pnRow, pnColumn, &errMsg);
if( ret != SQLITE_OK )
{
fprintf(stderr, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
DPRINTF(E_ERROR, L_DB_SQL, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
if (errMsg)
sqlite3_free(errMsg);
}

View File

@ -320,6 +320,21 @@ Send416(struct upnphttp * h)
CloseSocket_upnphttp(h);
}
/* very minimalistic 500 error message */
static void
Send500(struct upnphttp * h)
{
static const char body500[] =
"<HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>"
"<BODY><H1>Internal Server Error</H1>Server encountered "
"and Internal Error.</BODY></HTML>\r\n";
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 500, "Internal Server Errror",
body500, sizeof(body500) - 1);
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
}
/* very minimalistic 501 error message */
static void
Send501(struct upnphttp * h)
@ -356,12 +371,10 @@ sendXMLdesc(struct upnphttp * h, char * (f)(int *))
desc = f(&len);
if(!desc)
{
static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
"</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
DPRINTF(E_ERROR, L_HTTP, "Failed to generate XML description\n");
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 500, "Internal Server Error",
error500, sizeof(error500)-1);
Send500(h);
free(desc);
return;
}
else
{
@ -956,7 +969,7 @@ SendResp_albumArt(struct upnphttp * h, char * object)
char header[1500];
char sql_buf[256];
char **result;
int rows;
int rows = 0;
char *path;
char date[30];
time_t curtime = time(NULL);
@ -974,7 +987,7 @@ SendResp_albumArt(struct upnphttp * h, char * object)
strip_ext(object);
sprintf(sql_buf, "SELECT PATH from ALBUM_ART where ID = %s", object);
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
sql_get_table(db, sql_buf, &result, &rows, NULL);
if( !rows )
{
DPRINTF(E_WARN, L_HTTP, "ALBUM_ART ID %s not found, responding ERROR 404\n", object);
@ -1032,7 +1045,7 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
char header[1500];
char sql_buf[256];
char **result;
int rows;
int rows = 0;
char *path;
char date[30];
time_t curtime = time(NULL);
@ -1050,7 +1063,7 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
strip_ext(object);
sprintf(sql_buf, "SELECT PATH from DETAILS where ID = '%s'", object);
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
sql_get_table(db, sql_buf, &result, &rows, NULL);
if( !rows )
{
DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
@ -1120,18 +1133,38 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
char *key, *val;
char *saveptr=NULL, *item=NULL;
char *pixelshape=NULL;
sqlite_int64 id;
int rows=0, ret;
ExifData *ed;
ExifLoader *l;
image * imsrc;
image * imdst;
id = strtoll(object, NULL, 10);
sprintf(sql_buf, "SELECT PATH, RESOLUTION, THUMBNAIL from DETAILS where ID = '%lld'", id);
ret = sql_get_table(db, sql_buf, &result, &rows, NULL);
if( (ret != SQLITE_OK) )
{
DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %lld!\n", id);
Send500(h);
return;
}
if( !rows || (access(result[3], F_OK) != 0) )
{
DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
sqlite3_free_table(result);
Send404(h);
return;
}
#if USE_FORK
pid_t newpid = 0;
newpid = fork();
if( newpid )
return;
goto resized_error;
#endif
memset(header, 0, 1500);
file_path = result[3];
resolution = result[4];
tn = result[5];
path = strdup(object);
if( strtok_r(path, "?", &saveptr) )
@ -1164,7 +1197,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
}
item = strtok_r(NULL, "&", &saveptr);
}
strip_ext(path);
free(path);
if( h->reqflags & FLAG_XFERSTREAMING || h->reqflags & FLAG_RANGE )
{
@ -1173,19 +1206,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
goto resized_error;
}
sprintf(sql_buf, "SELECT PATH, RESOLUTION, THUMBNAIL from DETAILS where ID = '%s'", path);
ret = sql_get_table(db, sql_buf, &result, &rows, NULL);
if( (ret != SQLITE_OK) || !rows || (access(result[3], F_OK) != 0) )
{
DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %s!\n", path);
free(path);
goto resized_error;
}
file_path = result[3];
resolution = result[4];
tn = result[5];
DPRINTF(E_INFO, L_HTTP, "Serving resized image for ObjectId: %s [%s]\n", path, file_path);
free(path);
DPRINTF(E_INFO, L_HTTP, "Serving resized image for ObjectId: %lld [%s]\n", id, file_path);
/* Resizing from a thumbnail is much faster than from a large image */
#ifdef __sparc__
@ -1204,7 +1225,6 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
if( ed )
exif_data_unref(ed);
Send404(h);
sqlite3_free_table(result);
goto resized_error;
}
imsrc = image_new_from_jpeg(NULL, 0, (char *)ed->data, ed->size);
@ -1217,7 +1237,6 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
if( !imsrc )
{
Send404(h);
sqlite3_free_table(result);
goto resized_error;
}
/* Figure out the best destination resolution we can use */
@ -1260,10 +1279,11 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
DPRINTF(E_INFO, L_HTTP, "Done serving %s\n", file_path);
image_free(imsrc);
image_free(imdst);
sqlite3_free_table(result);
resized_error:
sqlite3_free_table(result);
#if USE_FORK
_exit(0);
if( !newpid )
_exit(0);
#endif
}
@ -1274,40 +1294,57 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
char hdr_buf[512];
char sql_buf[256];
char **result;
int rows;
int rows, ret;
char date[30];
time_t curtime = time(NULL);
off_t total, offset, size;
char *path, *mime, *dlna;
sqlite_int64 id;
int sendfh;
static struct { sqlite_int64 id; char path[PATH_MAX]; char mime[32]; char dlna[64]; } last_file = { 0 };
id = strtoll(object, NULL, 10);
if( id != last_file.id )
{
sprintf(sql_buf, "SELECT PATH, MIME, DLNA_PN from DETAILS where ID = '%lld'", id);
ret = sql_get_table(db, sql_buf, &result, &rows, NULL);
if( (ret != SQLITE_OK) )
{
DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %lld!\n", id);
Send500(h);
return;
}
if( !rows )
{
DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
Send404(h);
goto error;
}
/* Cache the result */
last_file.id = id;
strncpy(last_file.path, result[3], sizeof(last_file.path)-1);
if( result[4] )
strncpy(last_file.mime, result[4], sizeof(last_file.mime)-1);
else
last_file.mime[0] = '\0';
if( result[5] )
snprintf(last_file.dlna, sizeof(last_file.dlna), "DLNA.ORG_PN=%s", result[5]);
else
last_file.dlna[0] = '\0';
sqlite3_free_table(result);
}
#if USE_FORK
pid_t newpid = 0;
newpid = fork();
if( newpid )
return;
//goto error;
#endif
memset(header, 0, 1500);
strip_ext(object);
sprintf(sql_buf, "SELECT PATH, MIME, DLNA_PN from DETAILS where ID = '%s'", object);
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
if( !rows )
{
DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
Send404(h);
sqlite3_free_table(result);
goto done_dlna;
}
path = result[3];
mime = result[4];
dlna = result[5];
DPRINTF(E_INFO, L_HTTP, "Serving DetailID: %s [%s]\n", object, path);
DPRINTF(E_INFO, L_HTTP, "Serving DetailID: %lld [%s]\n", id, last_file.path);
if( h->reqflags & FLAG_XFERSTREAMING )
{
if( strncmp(mime, "image", 5) == 0 )
if( strncmp(last_file.mime, "image", 5) == 0 )
{
DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
Send406(h);
@ -1322,7 +1359,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
Send400(h);
goto error;
}
if( strncmp(mime, "image", 5) != 0 )
if( strncmp(last_file.mime, "image", 5) != 0 )
{
DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Interactive without an image!\n");
Send406(h);
@ -1332,16 +1369,16 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
offset = h->req_RangeStart;
sendfh = open(path, O_RDONLY);
sendfh = open(last_file.path, O_RDONLY);
if( sendfh < 0 ) {
DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path);
DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", last_file.path);
goto error;
}
size = lseek(sendfh, 0, SEEK_END);
lseek(sendfh, 0, SEEK_SET);
sprintf(header, "HTTP/1.1 20%c OK\r\n"
"Content-Type: %s\r\n", (h->reqflags & FLAG_RANGE ? '6' : '0'), mime);
"Content-Type: %s\r\n", (h->reqflags & FLAG_RANGE ? '6' : '0'), last_file.mime);
if( h->reqflags & FLAG_RANGE )
{
if( !h->req_RangeEnd )
@ -1391,13 +1428,13 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
}
else if( h->reqflags & FLAG_XFERBACKGROUND )
{
if( strncmp(mime, "image", 5) == 0 )
if( strncmp(last_file.mime, "image", 5) == 0 )
strcat(header, "transferMode.dlna.org: Background\r\n");
}
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
{
if( (strncmp(mime, "video", 5) == 0) ||
(strncmp(mime, "audio", 5) == 0) )
if( (strncmp(last_file.mime, "video", 5) == 0) ||
(strncmp(last_file.mime, "audio", 5) == 0) )
{
strcat(header, "transferMode.dlna.org: Streaming\r\n");
}
@ -1413,7 +1450,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
"EXT:\r\n"
"contentFeatures.dlna.org: DLNA.ORG_PN=%s\r\n"
"Server: RAIDiator/4.1, UPnP/1.0, MiniDLNA/1.0\r\n\r\n",
date, dlna);
date, last_file.dlna);
strcat(header, hdr_buf);
if( (send_data(h, header, strlen(header)) == 0) && (h->req_command != EHead) && (sendfh > 0) )
@ -1423,9 +1460,8 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
close(sendfh);
error:
sqlite3_free_table(result);
done_dlna:
#if USE_FORK
_exit(0);
if( !newpid )
_exit(0);
#endif
}