1 /*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2010 Oracle. All rights reserved.
5 *
6 */
7
8 /*
9 ** This file implements the sqlite bfile extension for Berkeley DB.
10 */
11 #include <assert.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #ifdef BFILE_USE_CAPIS
19 #include <bfile.h>
20 #endif
21 #include <sqlite3ext.h>
22 SQLITE_EXTENSION_INIT1
23
24 #define DIRECTORY "'BFILE_DIRECTORY'"
25 #define BFILE_PREFIX "B"
26 #define PATH_SEPARATOR "/"
27 #define INTERNAL_ERR_MSG "internal error"
28 #define UNIQUE_ERR_MSG "Directory already exist"
29 #define NOT_EXIST_ERR_MSG "Directory does not exist"
30
31 static void BFileCreateDirectoryFunc(sqlite3_context *, int, sqlite3_value **);
32 static void BFileReplaceDirectoryFunc(sqlite3_context *, int, sqlite3_value **);
33 static void BFileDropDirectoryFunc(sqlite3_context *, int, sqlite3_value **);
34 static void BFileNameFunc(sqlite3_context *, int, sqlite3_value **);
35 static void BFileFullPathFunc(sqlite3_context *, int, sqlite3_value **);
36 static int search_path_by_alias(sqlite3 *, char *, char *, int, char **);
37 static int get_full_path(sqlite3 *, char *, int, char **);
38
39 /*
40 * The BFileCreateDirectoryFunc() SQL function create a directory object.
41 */
BFileCreateDirectoryFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)42 static void BFileCreateDirectoryFunc(
43 sqlite3_context *context,
44 int argc,
45 sqlite3_value **argv)
46 {
47 sqlite3 *db;
48 sqlite3_stmt *stmt = NULL;
49 char *alias, *path;
50 int alias_size, path_size, rc = 0;
51 #define DIR_INS "insert into "DIRECTORY" values(?,?);"
52
53 assert(context != NULL && argv != NULL && argc == 2);
54
55 alias = (char *)sqlite3_value_text(argv[0]);
56 alias_size = sqlite3_value_bytes(argv[0]);
57 path = (char *)sqlite3_value_text(argv[1]);
58 path_size = sqlite3_value_bytes(argv[1]);
59
60 db = (sqlite3 *)sqlite3_user_data(context);
61
62 if (sqlite3_prepare_v2(db, DIR_INS, sizeof(DIR_INS) - 1, &stmt, NULL))
63 goto err;
64
65 if (sqlite3_bind_text(stmt, 1, alias, alias_size, SQLITE_STATIC))
66 goto err;
67
68 if (sqlite3_bind_text(stmt, 2, path, path_size, SQLITE_STATIC))
69 goto err;
70
71 if ((rc = sqlite3_step(stmt)) != SQLITE_DONE)
72 goto err;
73
74 sqlite3_finalize(stmt);
75
76 return;
77 err:
78 if (stmt)
79 sqlite3_finalize(stmt);
80
81 if (rc == SQLITE_CONSTRAINT)
82 sqlite3_result_error(context, UNIQUE_ERR_MSG, -1);
83 else
84 sqlite3_result_error(context, INTERNAL_ERR_MSG, -1);
85 }
86
87 /*
88 * The BFileReplaceDirectoryFunc() SQL function replace a directory object.
89 */
BFileReplaceDirectoryFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)90 static void BFileReplaceDirectoryFunc(
91 sqlite3_context *context,
92 int argc,
93 sqlite3_value **argv)
94 {
95 sqlite3 *db;
96 sqlite3_stmt *stmt = NULL;
97 char *alias, *path;
98 int alias_size, path_size, changed = 1;
99 #define DIR_UPD "update "DIRECTORY" set PATH=? where ALIAS=?;"
100
101 assert(context != NULL && argv != NULL && argc == 2);
102
103 alias = (char *)sqlite3_value_text(argv[0]);
104 alias_size = sqlite3_value_bytes(argv[0]);
105 path = (char *)sqlite3_value_text(argv[1]);
106 path_size = sqlite3_value_bytes(argv[1]);
107
108 db = (sqlite3 *)sqlite3_user_data(context);
109
110 if (sqlite3_prepare(db, DIR_UPD, sizeof(DIR_UPD) - 1, &stmt, NULL))
111 goto err;
112
113 if (sqlite3_bind_text(stmt, 1, path, path_size, SQLITE_STATIC))
114 goto err;
115
116 if (sqlite3_bind_text(stmt, 2, alias, alias_size, SQLITE_STATIC))
117 goto err;
118
119 if (sqlite3_step(stmt) != SQLITE_DONE)
120 goto err;
121
122 if ((changed = sqlite3_changes(db)) < 1)
123 goto err;
124
125 sqlite3_finalize(stmt);
126
127 return;
128 err:
129 if (stmt)
130 sqlite3_finalize(stmt);
131
132 if (changed < 1)
133 sqlite3_result_error(context, NOT_EXIST_ERR_MSG, -1);
134 else
135 sqlite3_result_error(context, INTERNAL_ERR_MSG, -1);
136 }
137
138 /*
139 * The BFileDropDirectoryFunc() SQL function drop a directory object.
140 */
BFileDropDirectoryFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)141 static void BFileDropDirectoryFunc(
142 sqlite3_context *context,
143 int argc,
144 sqlite3_value **argv)
145 {
146 sqlite3 *db;
147 sqlite3_stmt *stmt = NULL;
148 char *alias;
149 int alias_size, changed = 1;
150 #define DIR_DEL "delete from "DIRECTORY" where ALIAS=?;"
151
152 assert(context != NULL && argv != NULL && argc == 1);
153
154 alias = (char *)sqlite3_value_text(argv[0]);
155 alias_size = sqlite3_value_bytes(argv[0]);
156
157 db = (sqlite3 *)sqlite3_user_data(context);
158
159 if (sqlite3_prepare(db, DIR_DEL, sizeof(DIR_DEL) - 1, &stmt, NULL))
160 goto err;
161
162 if (sqlite3_bind_text(stmt, 1, alias, alias_size, SQLITE_STATIC))
163 goto err;
164
165 if (sqlite3_step(stmt) != SQLITE_DONE)
166 goto err;
167
168 if ((changed = sqlite3_changes(db)) < 1)
169 goto err;
170
171 sqlite3_finalize(stmt);
172
173 return;
174 err:
175 if (stmt)
176 sqlite3_finalize(stmt);
177
178 if (changed < 1)
179 sqlite3_result_error(context, NOT_EXIST_ERR_MSG, -1);
180 else
181 sqlite3_result_error(context, INTERNAL_ERR_MSG, -1);
182 }
183
184 /*
185 * The BFileNameFunc() SQL function returns locator of a BFile.
186 */
BFileNameFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)187 static void BFileNameFunc(
188 sqlite3_context *context,
189 int argc,
190 sqlite3_value **argv)
191 {
192 int dir_alias_size, filename_size, n;
193 char *locator, *pLoc;
194
195 assert(context != NULL && argc == 2 && argv != NULL);
196
197 dir_alias_size = sqlite3_value_bytes(argv[0]);
198 filename_size = sqlite3_value_bytes(argv[1]);
199
200 if (filename_size == 0) {
201 sqlite3_result_null(context);
202 return;
203 }
204
205 n = strlen(BFILE_PREFIX) + 2 * sizeof(char) + dir_alias_size +
206 filename_size;
207
208 locator = (char *)sqlite3_malloc(n);
209 if (locator == NULL) {
210 sqlite3_result_error_nomem(context);
211 return;
212 }
213
214 /* prefix to avoid be converted to number */
215 pLoc = locator;
216 memcpy(pLoc, BFILE_PREFIX, strlen(BFILE_PREFIX));
217
218 pLoc += strlen(BFILE_PREFIX);
219 *pLoc = dir_alias_size & 0xff;
220 *(pLoc + 1) = (dir_alias_size >> 8) & 0xff;
221
222 pLoc += 2 * sizeof(char);
223 memcpy(pLoc, (char *)sqlite3_value_text(argv[0]), dir_alias_size);
224
225 pLoc += dir_alias_size;
226 memcpy(pLoc, (char *)sqlite3_value_text(argv[1]), filename_size);
227
228 sqlite3_result_blob(context, locator, n, SQLITE_TRANSIENT);
229 }
230
231 /*
232 * The BFileFullPathFunc() SQL function returns full path of a BFile
233 */
BFileFullPathFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)234 static void BFileFullPathFunc(
235 sqlite3_context *context,
236 int argc,
237 sqlite3_value **argv)
238 {
239 int rc;
240 sqlite3 *db;
241 int loc_size;
242 char *pLoc, *full_path;
243
244 assert(context != NULL && argc == 1 && argv != NULL);
245
246 loc_size = sqlite3_value_bytes(argv[0]);
247 if (loc_size == 0) {
248 sqlite3_result_null(context);
249 return;
250 }
251
252 pLoc = (char *)sqlite3_value_text(argv[0]);
253 db = (sqlite3 *)sqlite3_user_data(context);
254
255 rc = get_full_path(db, pLoc, loc_size, &full_path);
256 if (rc) {
257 if (rc == SQLITE_NOMEM)
258 sqlite3_result_error_nomem(context);
259 else
260 sqlite3_result_error(context, "internal error", -1);
261
262 return;
263 }
264
265 sqlite3_result_text(context, full_path, strlen(full_path), sqlite3_free);
266 }
267
268 /*
269 * The search_path_by_alias() function returns the path of an alias
270 */
search_path_by_alias(sqlite3 * db,char * zSql,char * alias,int alias_size,char ** old_path)271 static int search_path_by_alias(
272 sqlite3 *db,
273 char *zSql,
274 char *alias,
275 int alias_size,
276 char **old_path)
277 {
278 int rc;
279 sqlite3_stmt *stmt;
280 char *result;
281
282 assert(db != NULL && zSql != NULL && old_path != NULL && alias != NULL);
283 *old_path = NULL;
284
285 rc = sqlite3_prepare(db, zSql, strlen(zSql), &stmt, NULL);
286 if (rc) {
287 rc = SQLITE_ERROR;
288 goto err;
289 }
290
291 rc = sqlite3_bind_text(stmt, 1, alias, alias_size, SQLITE_STATIC);
292 if (rc) {
293 rc = SQLITE_ERROR;
294 goto err;
295 }
296
297 while ((rc = sqlite3_step(stmt)) == SQLITE_BUSY){};
298 if (rc == SQLITE_DONE) {
299 rc = SQLITE_OK;
300 goto err;
301 }
302
303 if (rc != SQLITE_ROW) {
304 rc = SQLITE_ERROR;
305 goto err;
306 }
307
308 result = (char *)sqlite3_column_text(stmt, 0);
309 *old_path = sqlite3_malloc(strlen(result) + 1);
310 if (*old_path == NULL)
311 rc = SQLITE_NOMEM;
312 else {
313 strcpy(*old_path, result);
314 rc = SQLITE_OK;
315 }
316
317 err:
318 if (stmt)
319 sqlite3_finalize(stmt);
320 return rc;
321 }
322
323 /*
324 * The get_full_path() function returns the full path of a locator
325 */
get_full_path(sqlite3 * db,char * pLoc,int loc_size,char ** pFull_path)326 static int get_full_path(
327 sqlite3 *db,
328 char *pLoc,
329 int loc_size,
330 char **pFull_path)
331 {
332 int rc;
333 char *sql = "select path from "DIRECTORY" where alias=?;";
334 int dir_alias_size, dir_size, filename_size;
335 char *dir_path;
336
337 assert(db != NULL && pLoc != NULL && loc_size > 0 &&
338 pFull_path != NULL);
339
340 if (loc_size <= strlen(BFILE_PREFIX) + 2 * sizeof(char))
341 return SQLITE_ERROR;
342
343 if (strncmp(pLoc, BFILE_PREFIX, strlen(BFILE_PREFIX)))
344 return SQLITE_ERROR;
345
346 pLoc += strlen(BFILE_PREFIX);
347
348 dir_alias_size = ((*(pLoc + 1)) << 8) | (*pLoc);
349 filename_size = loc_size - strlen(BFILE_PREFIX) - 2 * sizeof(char) -
350 dir_alias_size;
351 pLoc += 2 * sizeof(char);
352
353 if ((rc = search_path_by_alias(db, sql, pLoc, dir_alias_size,
354 &dir_path)))
355 return rc;
356
357 dir_size = dir_path == NULL ? dir_alias_size : strlen(dir_path);
358 *pFull_path = sqlite3_malloc(dir_size + filename_size +
359 strlen(PATH_SEPARATOR) + 1);
360 if (*pFull_path == NULL)
361 return SQLITE_NOMEM;
362
363 memcpy(*pFull_path, dir_path == NULL ? pLoc : dir_path, dir_size);
364 if (dir_path != NULL)
365 sqlite3_free(dir_path);
366
367 memcpy(*pFull_path + dir_size, PATH_SEPARATOR, strlen(PATH_SEPARATOR));
368 memcpy(*pFull_path + dir_size + strlen(PATH_SEPARATOR), pLoc +
369 dir_alias_size, filename_size);
370
371 *(*pFull_path + dir_size + strlen(PATH_SEPARATOR) + filename_size) = 0;
372
373 return SQLITE_OK;
374 }
375
376 /*
377 * SQLite invokes this routine once when it loads the extension.
378 * Create new functions, collating sequences, and virtual table
379 * modules here. This is usually the only exported symbol in
380 * the shared library.
381 */
382 #define FIND_DIRECTORY \
383 "SELECT COUNT(name) FROM sqlite_master \
384 WHERE name = "DIRECTORY" AND TYPE = 'table';"
385 #define CREATE_DIRECTORY \
386 "CREATE TABLE "DIRECTORY"( \
387 ALIAS TEXT PRIMARY KEY, \
388 PATH TEXT \
389 );"
390
391 #ifdef BFILE_USE_CAPIS
392 typedef struct BfileHdl BfileHdl;
393 struct BfileHdl {
394 char *full_path; /* full path of the BFILE */
395 int fd; /* file discriptor of the BFILE */
396 };
397
398 /*
399 * Access BFILE element of the current row in the row set
400 */
sqlite3_column_bfile(sqlite3_stmt * pStmt,int iCol,sqlite3_bfile ** ppBfile)401 SQLITE_API int sqlite3_column_bfile(
402 sqlite3_stmt *pStmt,
403 int iCol,
404 sqlite3_bfile **ppBfile
405 )
406 {
407 BfileHdl *pHdl;
408 char * pLoc;
409 int loc_size, rc;
410 sqlite3 *db;
411 #define IS_ERROR(rc) \
412 ((rc)!= SQLITE_OK && (rc) != SQLITE_ROW && (rc) != SQLITE_DONE)
413
414 if (pStmt == NULL || iCol < 0 || iCol >= sqlite3_column_count(pStmt)
415 || ppBfile == NULL)
416 return SQLITE_ERROR;
417
418 db = sqlite3_db_handle(pStmt);
419
420 /*
421 * If a memory allocation error occurs during the evaluation of any of
422 * these routines, a default value is returned. The default value is
423 * either the integer 0, the floating point number 0.0, or a NULL
424 * pointer. Subsequent calls to sqlite3_errcode() will return
425 * SQLITE_NOMEM.
426 */
427 pLoc = (char *)sqlite3_column_blob(pStmt, iCol);
428 if (pLoc == NULL) {
429 *ppBfile = NULL;
430 rc = sqlite3_errcode(db);
431 return (IS_ERROR(rc) ? SQLITE_ERROR : SQLITE_OK);
432 }
433
434 pHdl = sqlite3_malloc(sizeof(BfileHdl));
435 if (pHdl == NULL) {
436 *ppBfile = NULL;
437 return SQLITE_ERROR;
438 }
439
440 pHdl->fd = -1;
441
442 loc_size = sqlite3_column_bytes(pStmt, iCol);
443
444 rc = get_full_path(db, pLoc, loc_size,&(pHdl->full_path));
445
446 if (rc) {
447 if (pHdl != NULL)
448 sqlite3_free(pHdl);
449 *ppBfile = NULL;
450 return SQLITE_ERROR;
451 }
452
453 *ppBfile = (sqlite3_bfile *)pHdl;
454
455 return SQLITE_OK;
456 }
457
458 /*
459 * Open a BFILE
460 */
sqlite3_bfile_open(sqlite3_bfile * pBfile)461 SQLITE_API int sqlite3_bfile_open(
462 sqlite3_bfile *pBfile)
463 {
464 BfileHdl *pHdl;
465
466 if (pBfile == NULL)
467 return SQLITE_OK;
468
469
470 pHdl = (BfileHdl *)pBfile;
471
472 if (pHdl->fd == -1)
473 pHdl->fd = open(pHdl->full_path, O_RDONLY);
474
475 return SQLITE_OK;
476 }
477
478 /*
479 * Check if file is open using this BFLIE
480 */
sqlite3_bfile_close(sqlite3_bfile * pBfile)481 SQLITE_API int sqlite3_bfile_close(
482 sqlite3_bfile *pBfile)
483 {
484 BfileHdl *pHdl;
485
486 if (pBfile == NULL)
487 return SQLITE_OK;
488
489 pHdl = (BfileHdl *)pBfile;
490
491 close(pHdl->fd);
492
493 pHdl->fd = -1;
494
495 return SQLITE_OK;
496 }
497
498 /*
499 * Check if file is open using this BFLIE
500 */
sqlite3_bfile_is_open(sqlite3_bfile * pBfile,int * open)501 SQLITE_API int sqlite3_bfile_is_open(
502 sqlite3_bfile *pBfile,
503 int *open)
504 {
505 BfileHdl *pHdl;
506 int rc, exist;
507
508 if (open == NULL)
509 return SQLITE_ERROR;
510
511 if (pBfile == NULL) {
512 *open = 0;
513 return SQLITE_OK;
514 }
515
516 rc = sqlite3_bfile_file_exists(pBfile, &exist);
517 if (rc != SQLITE_OK || exist != 1) {
518 *open = 0;
519 return SQLITE_ERROR;
520 }
521
522 pHdl = (BfileHdl *)pBfile;
523
524 *open = (pHdl->fd == -1) ? 0 : 1;
525
526 return SQLITE_OK;
527 }
528
529 /*
530 * Read a portion of a BFILE
531 */
sqlite3_bfile_read(sqlite3_bfile * pBfile,void * z,int nSize,off_t iOffset,int * nRead)532 SQLITE_API int sqlite3_bfile_read(
533 sqlite3_bfile *pBfile,
534 void *z,
535 int nSize,
536 off_t iOffset,
537 int *nRead)
538 {
539 BfileHdl *pHdl;
540 int rc;
541 off_t size;
542
543 if (z == NULL || nSize <= 0 || iOffset < 0 || nRead == NULL)
544 return SQLITE_ERROR;
545
546 rc = sqlite3_bfile_size(pBfile, &size);
547 if (rc != SQLITE_OK || iOffset > size) {
548 *nRead = 0;
549 return SQLITE_ERROR;
550 }
551
552 if (pBfile == NULL) {
553 *(char *)z = 0;
554 *nRead = 0;
555 return SQLITE_OK;
556 }
557
558 pHdl = (BfileHdl *)pBfile;
559
560 if (pHdl->fd == -1) {
561 *nRead = 0;
562 return SQLITE_ERROR;
563 }
564
565 if ((rc = lseek(pHdl->fd, iOffset, SEEK_SET)) == -1) {
566 *nRead = 0;
567 return SQLITE_ERROR;
568 }
569
570 if ((*nRead = read(pHdl->fd, z, nSize)) == -1) {
571 *nRead = 0;
572 return SQLITE_ERROR;
573 }
574
575 return SQLITE_OK;
576 }
577
578 /*
579 * Check if a file exists
580 */
sqlite3_bfile_file_exists(sqlite3_bfile * pBfile,int * exist)581 SQLITE_API int sqlite3_bfile_file_exists(
582 sqlite3_bfile *pBfile,
583 int *exist
584 )
585 {
586 BfileHdl *pHdl;
587
588 if (exist == NULL)
589 return SQLITE_ERROR;
590
591 if (pBfile == NULL) {
592 *exist = 0;
593 return SQLITE_OK;
594 }
595
596 pHdl = (BfileHdl *)pBfile;
597
598 if ((pHdl->fd != -1) || (!access(pHdl->full_path, F_OK)))
599 *exist = 1;
600 else
601 *exist = 0;
602
603 return SQLITE_OK;
604 }
605
606 /*
607 * Get File size of a BFILE
608 */
sqlite3_bfile_size(sqlite3_bfile * pBfile,off_t * size)609 SQLITE_API int sqlite3_bfile_size(
610 sqlite3_bfile *pBfile,
611 off_t *size)
612 {
613 BfileHdl *pHdl;
614 struct stat st;
615 int rc;
616
617 if (size == NULL)
618 return SQLITE_ERROR;
619
620 if (pBfile == NULL) {
621 *size = 0;
622 return SQLITE_OK;
623 }
624
625 pHdl = (BfileHdl *)pBfile;
626
627 memset(&st, 0, sizeof(st));
628
629 if ((rc = stat(pHdl->full_path, &st)) == -1) {
630 *size = 0;
631 return SQLITE_ERROR;
632 }
633
634 *size = st.st_size;
635
636 return SQLITE_OK;
637 }
638
639 /*
640 * Finalize a BFILE
641 */
sqlite3_bfile_final(sqlite3_bfile * pBfile)642 SQLITE_API int sqlite3_bfile_final(
643 sqlite3_bfile *pBfile)
644 {
645 BfileHdl *pHdl;
646
647 pHdl = (BfileHdl *)pBfile;
648
649 if (pHdl != NULL) {
650 if (pHdl->full_path != NULL)
651 sqlite3_free(pHdl->full_path);
652
653 sqlite3_free(pHdl);
654 }
655
656 return SQLITE_OK;
657 }
658 #else
659 typedef struct BfileHdl BfileHdl;
660 struct BfileHdl {
661 char *full_path; /* full path of the BFILE */
662 int fd; /* file discriptor of the BFILE*/
663 char *zBuf; /* read buf */
664 int zBufSiz; /* read buf size */
665 };
666
667 /*
668 * Open a BFILE
669 */
BFileOpenFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)670 static void BFileOpenFunc(
671 sqlite3_context *context,
672 int argc,
673 sqlite3_value **argv)
674 {
675 int rc;
676 sqlite3 *db; /* db handle */
677 int loc_size; /* BFile locater size */
678 int fd; /* file descriptor */
679 char *pLoc; /* BFile locater */
680 char *full_path; /* full path */
681 BfileHdl *pHdl; /* BFile handle */
682
683 assert(context != NULL && argc == 1 && argv != NULL);
684 full_path = NULL, pHdl = NULL;
685 fd = -1;
686
687 loc_size = sqlite3_value_bytes(argv[0]);
688 if (loc_size <= strlen(BFILE_PREFIX)) {
689 sqlite3_result_int(context, 0);
690 return;
691 }
692
693 db = (sqlite3 *)sqlite3_user_data(context);
694 pLoc = (char *)sqlite3_value_text(argv[0]);
695 assert(db != NULL && pLoc != NULL);
696
697 rc = get_full_path(db, pLoc, loc_size, &full_path);
698 if (rc)
699 goto err;
700
701 fd = open(full_path, O_RDONLY);
702 if (fd == -1) {
703 if (errno == ENOENT)
704 sqlite3_result_int(context, 0);
705 else
706 rc = -1;
707 goto err;
708 }
709
710 pHdl = sqlite3_malloc(sizeof(BfileHdl));
711 if (pHdl == NULL) {
712 rc = SQLITE_NOMEM;
713 goto err;
714 }
715
716 memset(pHdl, 0, sizeof(BfileHdl));
717 pHdl->full_path = full_path;
718 pHdl->fd = fd;
719 sqlite3_result_int64(context, (sqlite3_int64)pHdl);
720 return;
721
722 err:
723 if (rc == SQLITE_NOMEM)
724 sqlite3_result_error_nomem(context);
725 else if (rc)
726 sqlite3_result_error(context, "internal error", -1);
727
728 if (pHdl != NULL)
729 sqlite3_free(pHdl);
730
731 if (fd >= 0)
732 close(fd);
733
734 if (full_path != NULL)
735 sqlite3_free(full_path);
736 }
737
738 /*
739 * Close a BFILE
740 */
BFileCloseFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)741 static void BFileCloseFunc(
742 sqlite3_context *context,
743 int argc,
744 sqlite3_value **argv)
745 {
746 BfileHdl *pHdl;
747
748 assert(context != NULL && argc == 1 && argv != NULL);
749
750 pHdl = (BfileHdl*)sqlite3_value_int64(argv[0]);
751 if (pHdl != NULL) {
752 if (pHdl->fd >= 0)
753 close(pHdl->fd);
754 if (pHdl->full_path != NULL)
755 sqlite3_free(pHdl->full_path);
756 if (pHdl->zBuf != NULL)
757 sqlite3_free(pHdl->zBuf);
758 sqlite3_free(pHdl);
759 }
760 }
761
__bfile_get_size(char * full_path,off_t * size)762 static int __bfile_get_size(
763 char *full_path,
764 off_t *size)
765 {
766 struct stat st;
767 int rc;
768
769 assert(full_path != NULL && size != NULL);
770 memset(&st, 0, sizeof(st));
771
772 if ((rc = stat(full_path, &st)) == -1) {
773 *size = 0;
774 return SQLITE_ERROR;
775 }
776
777 *size = st.st_size;
778
779 return SQLITE_OK;
780 }
781
782 /*
783 * Read a portion of a BFILE
784 */
BFileReadFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)785 static void BFileReadFunc(
786 sqlite3_context *context,
787 int argc,
788 sqlite3_value **argv)
789 {
790 int rc;
791 BfileHdl *pHdl; /* BFile handle */
792 int nSize; /* up to nSize bytes would be read */
793 int nRead; /* the number of bytes read actually */
794 off_t size; /* the size of the underlying file */
795 off_t iOffset; /* the position from which read would start */
796 char *z; /* the pointer of read buffer */
797
798 assert(context != NULL && argc == 3 && argv != NULL);
799
800 pHdl = (BfileHdl*)sqlite3_value_int64(argv[0]);
801 if (pHdl == NULL || pHdl->full_path == NULL || pHdl->fd < 0) {
802 sqlite3_result_error(context, "invalid bfile handle", -1);
803 return;
804 }
805
806 nSize = sqlite3_value_int(argv[1]);
807 iOffset = (off_t)sqlite3_value_int(argv[2]);
808
809 rc = __bfile_get_size(pHdl->full_path, &size);
810 if (rc != SQLITE_OK) {
811 sqlite3_result_error(context, "internal error", -1);
812 return;
813 } else if (nSize <= 0) {
814 sqlite3_result_error(context, "invalid size", -1);
815 return;
816 } else if (iOffset < 0) {
817 sqlite3_result_error(context, "invalid offset", -1);
818 return;
819 } else if (iOffset > size) {
820 sqlite3_result_null(context);
821 return;
822 }
823
824 if ((rc = lseek(pHdl->fd, iOffset, SEEK_SET)) == -1) {
825 sqlite3_result_error(context, "disk I/O error", -1);
826 return;
827 }
828
829 if (pHdl->zBuf == NULL || pHdl->zBufSiz < nSize) {
830 z = pHdl->zBuf == NULL ? sqlite3_malloc(nSize) :
831 sqlite3_realloc(pHdl->zBuf, nSize);
832 if (z == NULL) {
833 sqlite3_result_error_nomem(context);
834 return;
835 }
836 pHdl->zBuf = z;
837 pHdl->zBufSiz = nSize;
838 }
839
840 if ((nRead = read(pHdl->fd, pHdl->zBuf, nSize)) == -1) {
841 sqlite3_result_error(context, "disk I/O error", -1);
842 return;
843 }
844
845 sqlite3_result_blob(context, pHdl->zBuf, nRead, SQLITE_STATIC);
846 }
847
848 /*
849 * Get File size of a BFILE
850 */
BFileSizeFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)851 static void BFileSizeFunc(
852 sqlite3_context *context,
853 int argc,
854 sqlite3_value **argv)
855 {
856 int rc;
857 sqlite3 *db;
858 int loc_size;
859 off_t size;
860 char *pLoc, *full_path;
861
862 assert(context != NULL && argc == 1 && argv != NULL);
863 full_path = NULL;
864
865 loc_size = sqlite3_value_bytes(argv[0]);
866 if (loc_size <= strlen(BFILE_PREFIX)) {
867 sqlite3_result_int(context, -1);
868 return;
869 }
870
871 db = (sqlite3 *)sqlite3_user_data(context);
872 pLoc = (char *)sqlite3_value_text(argv[0]);
873 assert(db != NULL && pLoc != NULL);
874
875 rc = get_full_path(db, pLoc, loc_size, &full_path);
876 if (rc) {
877 if (rc == SQLITE_NOMEM)
878 sqlite3_result_error_nomem(context);
879 else
880 sqlite3_result_error(context, "internal error", -1);
881 return;
882 }
883
884 /* check existence, if not exits at at set size as -1 */
885 if (access(full_path, F_OK))
886 sqlite3_result_int(context, -1);
887 else if (__bfile_get_size(full_path, &size) == SQLITE_OK)
888 sqlite3_result_int(context, size);
889 else
890 sqlite3_result_error(context, "internal error", -1);
891
892 sqlite3_free(full_path);
893 }
894 #endif
895
sqlite3_extension_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)896 int sqlite3_extension_init(
897 sqlite3 *db,
898 char **pzErrMsg,
899 const sqlite3_api_routines *pApi)
900 {
901 int rc;
902 sqlite3_stmt *pSelect;
903
904 SQLITE_EXTENSION_INIT2(pApi)
905
906 sqlite3_create_function(db, "BFILE_NAME", 2, SQLITE_ANY, 0,
907 BFileNameFunc, 0, 0);
908
909 sqlite3_create_function(db, "BFILE_FULLPATH", 1, SQLITE_ANY, db,
910 BFileFullPathFunc, 0, 0);
911
912 sqlite3_create_function(db, "BFILE_CREATE_DIRECTORY", 2, SQLITE_ANY,
913 db, BFileCreateDirectoryFunc, 0, 0);
914
915 sqlite3_create_function(db, "BFILE_REPLACE_DIRECTORY", 2, SQLITE_ANY,
916 db, BFileReplaceDirectoryFunc, 0, 0);
917
918 sqlite3_create_function(db, "BFILE_DROP_DIRECTORY", 1, SQLITE_ANY,
919 db, BFileDropDirectoryFunc, 0, 0);
920
921 #ifndef BFILE_USE_CAPIS
922 /* Bfile functions instead of c version */
923 sqlite3_create_function(db, "BFILE_OPEN", 1, SQLITE_ANY, db,
924 BFileOpenFunc, 0, 0);
925
926 sqlite3_create_function(db, "BFILE_CLOSE", 1, SQLITE_ANY, 0,
927 BFileCloseFunc, 0, 0);
928
929 sqlite3_create_function(db, "BFILE_READ", 3, SQLITE_ANY, 0,
930 BFileReadFunc, 0, 0);
931
932 sqlite3_create_function(db, "BFILE_SIZE", 1, SQLITE_ANY, db,
933 BFileSizeFunc, 0, 0);
934 #endif
935
936 rc = sqlite3_prepare(db, FIND_DIRECTORY, -1, &pSelect, 0);
937 if (rc != SQLITE_OK || !pSelect)
938 return rc;
939
940 rc = sqlite3_step(pSelect);
941 if (rc == SQLITE_ROW && sqlite3_column_int(pSelect, 0) == 0)
942 rc = sqlite3_exec(db, CREATE_DIRECTORY, 0, 0, pzErrMsg);
943
944 rc = sqlite3_finalize(pSelect);
945
946 return rc;
947 }
948