From: Eric Bollengier Date: Tue, 5 Feb 2008 08:22:37 +0000 (+0000) Subject: ebl update accurate project X-Git-Tag: Release-7.0.0~5066 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=0207283784ca7164be18ecb14a61c3220a0d6b4d;p=bacula%2Fbacula ebl update accurate project git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@6369 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/patches/testing/project-accurate-backup.patch b/bacula/patches/testing/project-accurate-backup.patch new file mode 100644 index 0000000000..95a05fd1f6 --- /dev/null +++ b/bacula/patches/testing/project-accurate-backup.patch @@ -0,0 +1,583 @@ +Index: src/dird/backup.c +=================================================================== +--- src/dird/backup.c (révision 6368) ++++ src/dird/backup.c (copie de travail) +@@ -44,6 +44,7 @@ + #include "bacula.h" + #include "dird.h" + #include "ua.h" ++#include "findlib/find.h" + + /* Commands sent to File daemon */ + static char backupcmd[] = "backup\n"; +@@ -97,6 +98,286 @@ + } + + /* ++ * We are called here for each record that matches the above ++ * SQL query -- that is for each file contained in the Catalog ++ * that was not marked earlier. This means that the file in ++ * question is a missing file (in the Catalog but not on Disk). ++ */ ++static int missing_handler(void *ctx, int num_fields, char **row) ++{ ++ JCR *jcr = (JCR *)ctx; ++ ++ if (job_canceled(jcr)) { ++ return 1; ++ } ++ ++ /* TODO: return the list to the FD */ ++ Qmsg(jcr, M_INFO, 0, " %s%s\n", row[0]?row[0]:"", row[1]?row[1]:""); ++ return 0; ++} ++ ++/* ++ * Accurate backup mode ++ * 1. Receive the list of all files including those backed up to the Dir ++ * 2. Dir computes files and deleted files. ++ * 3. Dir sends list of additional files (new files) to backup, and list of files ++ * deleted. ++ * ++ * Cleanup attributes (don't use atime, inode etc..) ++ * Need to insert file and attributes to temp table ++ * Batch compare files and attributes ++ * ++ * ++ */ ++bool accurate_compute_files(JCR *jcr) ++{ ++ BSOCK *fd; ++ int n, len; ++ FILE_DBR fdbr; ++ struct stat statf; /* file stat */ ++ struct stat statc; /* catalog stat */ ++ int stat = JS_Terminated; ++ char buf[MAXSTRING]; ++ POOLMEM *fname = get_pool_memory(PM_MESSAGE); ++ int do_Digest = CRYPTO_DIGEST_NONE; ++ int32_t file_index = 0; ++ JobId_t JobId=0; /* TODO: compute the job key in new table */ ++ ++ memset(&fdbr, 0, sizeof(FILE_DBR)); ++ fd = jcr->file_bsock; ++ fdbr.JobId = JobId; ++ jcr->FileIndex = 0; ++ ++ Dmsg0(20, "bdird: waiting to receive file attributes\n"); ++ /* ++ * Get Attributes and Signature from File daemon ++ * We expect: ++ * FileIndex ++ * Stream ++ * Options or Digest (MD5/SHA1) ++ * Filename ++ * Attributes ++ * Link name ??? ++ */ ++ while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) { ++ int stream; ++ char *attr, *p, *fn; ++ char Opts_Digest[MAXSTRING]; /* Verify Opts or MD5/SHA1 digest */ ++ ++ if (job_canceled(jcr)) { ++ return false; ++ } ++ fname = check_pool_memory_size(fname, fd->msglen); ++ jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen); ++ Dmsg1(20, "Atts+Digest=%s\n", fd->msg); ++ if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream, ++ fname)) != 3) { ++ Jmsg3(jcr, M_FATAL, 0, _("birdmsglen, fd->msg); ++ return false; ++ } ++ /* ++ * We read the Options or Signature into fname ++ * to prevent overrun, now copy it to proper location. ++ */ ++ bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest)); ++ p = fd->msg; ++ skip_nonspaces(&p); /* skip FileIndex */ ++ skip_spaces(&p); ++ skip_nonspaces(&p); /* skip Stream */ ++ skip_spaces(&p); ++ skip_nonspaces(&p); /* skip Opts_Digest */ ++ p++; /* skip space */ ++ fn = fname; ++ while (*p != 0) { ++ *fn++ = *p++; /* copy filename */ ++ } ++ *fn = *p++; /* term filename and point to attribs */ ++ attr = p; ++ /* ++ * Got attributes stream, decode it ++ */ ++ if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) { ++ int32_t LinkFIf, LinkFIc; ++ Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr); ++ jcr->JobFiles++; ++ jcr->FileIndex = file_index; /* remember attribute file_index */ ++ decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */ ++ do_Digest = CRYPTO_DIGEST_NONE; ++ pm_strcpy(jcr->fname, fname); /* move filename into JCR */ ++ ++ Dmsg3(040, "dirdfname, attr); ++ ++ /* ++ * Find equivalent record in the database ++ */ ++ fdbr.FileId = 0; ++// if (!db_get_file_attributes_record(jcr, jcr->db, jcr->fname, ++// &jcr->previous_jr, &fdbr)) { ++ if (1) { ++ Jmsg(jcr, M_INFO, 0, _("New file: %s\n"), jcr->fname); ++ Dmsg1(020, _("File not in catalog: %s\n"), jcr->fname); ++ continue; ++ } else { ++ /* ++ * mark file record as visited by stuffing the ++ * current JobId, which is unique, into the MarkId field. ++ */ ++ db_mark_file_record(jcr, jcr->db, fdbr.FileId, jcr->JobId); ++ } ++ ++ Dmsg3(400, "Found %s in catalog. inx=%d Opts=%s\n", jcr->fname, ++ file_index, Opts_Digest); ++ decode_stat(fdbr.LStat, &statc, &LinkFIc); /* decode catalog stat */ ++ ++ // TODO: for each JS_Differences, send it to FD for backup ++ /* ++ * Loop over options supplied by user and verify the ++ * fields he requests. ++ */ ++ for (p=Opts_Digest; *p; p++) { ++ char ed1[30], ed2[30]; ++ switch (*p) { ++ case 'i': /* compare INODEs */ ++ if (statc.st_ino != statf.st_ino) { ++ Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"), ++ edit_uint64((uint64_t)statc.st_ino, ed1), ++ edit_uint64((uint64_t)statf.st_ino, ed2)); ++ stat = JS_Differences; ++ } ++ break; ++ case 'p': /* permissions bits */ ++ if (statc.st_mode != statf.st_mode) { ++ Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"), ++ (uint32_t)statc.st_mode, (uint32_t)statf.st_mode); ++ stat = JS_Differences; ++ } ++ break; ++ case 'n': /* number of links */ ++ if (statc.st_nlink != statf.st_nlink) { ++ Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"), ++ (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink); ++ stat = JS_Differences; ++ } ++ break; ++ case 'u': /* user id */ ++ if (statc.st_uid != statf.st_uid) { ++ Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"), ++ (uint32_t)statc.st_uid, (uint32_t)statf.st_uid); ++ stat = JS_Differences; ++ } ++ break; ++ case 'g': /* group id */ ++ if (statc.st_gid != statf.st_gid) { ++ Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"), ++ (uint32_t)statc.st_gid, (uint32_t)statf.st_gid); ++ stat = JS_Differences; ++ } ++ break; ++ case 's': /* size */ ++ if (statc.st_size != statf.st_size) { ++ Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"), ++ edit_uint64((uint64_t)statc.st_size, ed1), ++ edit_uint64((uint64_t)statf.st_size, ed2)); ++ stat = JS_Differences; ++ } ++ break; ++ case 'a': /* access time */ ++ if (statc.st_atime != statf.st_atime) { ++ Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n")); ++ stat = JS_Differences; ++ } ++ break; ++ case 'm': ++ if (statc.st_mtime != statf.st_mtime) { ++ Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n")); ++ stat = JS_Differences; ++ } ++ break; ++ case 'c': /* ctime */ ++ if (statc.st_ctime != statf.st_ctime) { ++ Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n")); ++ stat = JS_Differences; ++ } ++ break; ++ case 'd': /* file size decrease */ ++ if (statc.st_size > statf.st_size) { ++ Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"), ++ edit_uint64((uint64_t)statc.st_size, ed1), ++ edit_uint64((uint64_t)statf.st_size, ed2)); ++ stat = JS_Differences; ++ } ++ break; ++ case '5': /* compare MD5 */ ++ Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname); ++ do_Digest = CRYPTO_DIGEST_MD5; ++ break; ++ case '1': /* compare SHA1 */ ++ do_Digest = CRYPTO_DIGEST_SHA1; ++ break; ++ case ':': ++ case 'V': ++ default: ++ break; ++ } ++ } ++ /* ++ * Got Digest Signature from Storage daemon ++ * It came across in the Opts_Digest field. ++ */ ++ } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) { ++ Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest); ++ /* ++ * When ever we get a digest it MUST have been ++ * preceded by an attributes record, which sets attr_file_index ++ */ ++ if (jcr->FileIndex != (uint32_t)file_index) { ++ Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"), ++ file_index, jcr->FileIndex); ++ return false; ++ } ++ if (do_Digest != CRYPTO_DIGEST_NONE) { ++ db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest)); ++ if (strcmp(buf, fdbr.Digest) != 0) { ++ if (debug_level >= 10) { ++ Jmsg(jcr, M_INFO, 0, _(" %d not same. File=%s Cat=%s\n"), ++ stream, buf, fdbr.Digest); ++ } else { ++ Jmsg(jcr, M_INFO, 0, _(" %d differs.\n"), ++ stream); ++ } ++ stat = JS_Differences; ++ } ++ do_Digest = CRYPTO_DIGEST_NONE; ++ } ++ } ++// jcr->JobFiles = file_index; ++ } ++ if (is_bnet_error(fd)) { ++ berrno be; ++ Jmsg2(jcr, M_FATAL, 0, _("bdirdJobId); ++ /* missing_handler is called for each file found */ ++ db_sql_query(jcr->db, buf, missing_handler, (void *)jcr); ++ ++ free_pool_memory(fname); ++ ++ return true; ++} ++ ++/* + * Do a backup of the specified FileSet + * + * Returns: false on failure +@@ -231,6 +512,13 @@ + goto bail_out; + } + ++ /* ++ * If backup is in accurate mode, FD will send the list of ++ * all files. We have to store it, and compute witch files ++ * have been deleted and witch files have to be backuped. ++ */ ++ accurate_compute_files(jcr); ++ + /* Pickup Job termination data */ + stat = wait_for_job_termination(jcr); + db_write_batch_file_records(jcr); /* used by bulk batch file insert */ +Index: src/dird/inc_conf.c +=================================================================== +--- src/dird/inc_conf.c (révision 6368) ++++ src/dird/inc_conf.c (copie de travail) +@@ -94,6 +94,7 @@ + * Items that are valid in an Options resource + */ + static RES_ITEM options_items[] = { ++ {"accurate", store_opts, {0}, 0, 0, 0}, + {"compression", store_opts, {0}, 0, 0, 0}, + {"signature", store_opts, {0}, 0, 0, 0}, + {"verify", store_opts, {0}, 0, 0, 0}, +@@ -153,7 +154,8 @@ + INC_KW_NOATIME, + INC_KW_ENHANCEDWILD, + INC_KW_CHKCHANGES, +- INC_KW_STRIPPATH ++ INC_KW_STRIPPATH, ++ INC_KW_ACCURATE + }; + + /* +@@ -163,6 +165,7 @@ + * options given above. + */ + static struct s_kw FS_option_kw[] = { ++ {"accurate", INC_KW_ACCURATE}, + {"compression", INC_KW_COMPRESSION}, + {"signature", INC_KW_DIGEST}, + {"encryption", INC_KW_ENCRYPTION}, +@@ -251,6 +254,8 @@ + {"no", INC_KW_ENHANCEDWILD, "0"}, + {"yes", INC_KW_CHKCHANGES, "c"}, + {"no", INC_KW_CHKCHANGES, "0"}, ++ {"yes", INC_KW_ACCURATE, "C"}, ++ {"no", INC_KW_ACCURATE, "0"}, + {NULL, 0, 0} + }; + +Index: src/dird/dird_conf.c +=================================================================== +--- src/dird/dird_conf.c (révision 6368) ++++ src/dird/dird_conf.c (copie de travail) +@@ -319,6 +319,7 @@ + {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0}, + {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0}, + {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0}, ++ {"accuratebackup", store_bool, ITEM(res_job.accurate), 0,0,0}, + {NULL, NULL, {0}, 0, 0, 0} + }; + +@@ -618,6 +619,9 @@ + if (res->res_job.spool_size) { + sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1)); + } ++ if (res->res_job.JobType == JT_BACKUP) { ++ sendit(sock, _(" Accurate=%d\n"), res->res_job.accurate); ++ } + if (res->res_job.JobType == JT_MIGRATE) { + sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type); + } +Index: src/dird/dird_conf.h +=================================================================== +--- src/dird/dird_conf.h (révision 6368) ++++ src/dird/dird_conf.h (copie de travail) +@@ -400,6 +400,7 @@ + bool write_part_after_job; /* Set to write part after job in SD */ + bool enabled; /* Set if job enabled */ + bool OptimizeJobScheduling; /* Set if we should optimize Job scheduling */ ++ bool accurate; /* Set if it is an accurate backup job */ + + MSGS *messages; /* How and where to send messages */ + SCHED *schedule; /* When -- Automatic schedule */ +Index: src/filed/backup.c +=================================================================== +--- src/filed/backup.c (révision 6368) ++++ src/filed/backup.c (copie de travail) +@@ -49,7 +49,84 @@ + static void crypto_session_end(JCR *jcr); + static bool crypto_session_send(JCR *jcr, BSOCK *sd); + ++#define backup_stat(x,y,z) (x.z = y.z ; y.z = 0) ++ + /* ++ * Called by save_file when accept/discard file for backup ++ * TODO: we could add MD5/SHAX digest, but we have to compute it ++ * for all files. ++ */ ++static bool accurate_add_file(JCR *jcr, FF_PKT *ff_pkt, char *stats) ++{ ++ char *a=stats; ++ char attribs[MAXSTRING]; ++ uint32_t file_index=jcr->JobFiles; ++ BSOCK *dir = jcr->dir_bsock; ++ int stat; ++ ++ if (jcr->accurate == false) { ++ return true; ++ } ++ ++ if (!stats) { ++ file_index=0; ++ encode_stat(attribs, ff_pkt, 0); ++ a = attribs; ++ } ++ ++ if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) { ++ stat = dir->fsend("%d %d %s %s%c%s%c%s%c", file_index, ++ STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, ++ 0, a, 0, ff_pkt->link, 0); ++ } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE) { ++ /* Here link is the canonical filename (i.e. with trailing slash) */ ++ stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index, ++ STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link, ++ 0, a, 0, 0); ++ } else { ++ stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index, ++ STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, ++ 0, a, 0, 0); ++ } ++ ++ if (!stat) { ++ Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir)); ++ return 0; ++ } ++ ++ return true; ++} ++ ++/* build a fileset with new files from director */ ++static bool accurate_get_new_and_deleted_file_list(JCR *jcr) ++{ ++ if (jcr->accurate == false || job_canceled(jcr)) { ++ return true; ++ } ++ return true; ++} ++ ++/* send deleted file list to stored */ ++static bool accurate_send_deleted_list(JCR *jcr) ++{ ++ if (jcr->accurate == false || job_canceled(jcr)) { ++ return true; ++ } ++ return true; ++} ++ ++static bool accurate_send_file_list(JCR *jcr) ++{ ++ if (jcr->accurate == false || job_canceled(jcr)) { ++ return true; ++ } ++ Dmsg0(1, "Sending BNET_EOD\n"); ++ jcr->dir_bsock->signal(BNET_EOD); /* end of sending data */ ++ return true; ++} ++ ++ ++/* + * Find all the requested files and send them + * to the Storage daemon. + * +@@ -66,6 +143,7 @@ + BSOCK *sd; + bool ok = true; + // TODO landonf: Allow user to specify encryption algorithm ++ jcr->accurate=true; /* TODO: remove that */ + + sd = jcr->store_bsock; + +@@ -135,6 +213,20 @@ + set_jcr_job_status(jcr, JS_ErrorTerminated); + } + ++ /* start accurate stuffs */ ++ if (jcr->accurate) { ++ /* TODO: test job_canceled() */ ++ accurate_send_file_list(jcr); /* send all files to DIR */ ++ accurate_get_new_and_deleted_file_list(jcr); /* get a new incr fileset from DIR */ ++// set_find_options((FF_PKT *)jcr->ff, 0, 0); /* we backup all that director wants */ ++// if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, (void *)jcr)) { ++// ok = false; /* error */ ++// set_jcr_job_status(jcr, JS_ErrorTerminated); ++// } ++// accurate_send_file_list(jcr); /* send all new files to DIR */ ++ accurate_send_deleted_list(jcr); /* send deleted list to SD */ ++ } ++ + free_pool_memory(jcr->acl_text); + + stop_heartbeat_monitor(jcr); +@@ -355,9 +447,11 @@ + case FT_DIRNOCHG: + case FT_NOCHG: + Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); ++ accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */ + return 1; + case FT_ISARCH: + Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname); ++ accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */ + return 1; + case FT_NOOPEN: { + berrno be; +@@ -1109,6 +1203,9 @@ + } + unstrip_path(ff_pkt); + ++ /* list backuped files */ ++ accurate_add_file(jcr, ff_pkt, attribs); ++ + Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg); + if (!stat) { + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), +Index: src/filed/job.c +=================================================================== +--- src/filed/job.c (révision 6368) ++++ src/filed/job.c (copie de travail) +@@ -1087,6 +1087,9 @@ + case 'c': + fo->flags |= FO_CHKCHANGES; + break; ++ case 'C': ++ fo->flags |= FO_ACCURATE; ++ break; + default: + Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p); + break; +Index: src/cats/sql_create.c +=================================================================== +--- src/cats/sql_create.c (révision 6368) ++++ src/cats/sql_create.c (copie de travail) +@@ -829,6 +829,14 @@ + return true; + } + ++bool db_accurate_insert(JCR *jcr, B_DB *mdb, bool saved, const char *fname, struct stat *stat) ++{ ++ int len; ++ split_path_and_file(jcr, mdb, fname); ++ /* make like in Verify code */ ++ return true; ++} ++ + /* + * Create File record in B_DB + * +Index: src/jcr.h +=================================================================== +--- src/jcr.h (révision 6368) ++++ src/jcr.h (copie de travail) +@@ -208,6 +208,7 @@ + B_DB *db_batch; /* database pointer for batch insert */ + ATTR_DBR *ar; /* DB attribute record */ + guid_list *id_list; /* User/group id to name list */ ++ bool accurate; /* true if job is accurate */ + + void *plugin_ctx; + +Index: src/findlib/find.h +=================================================================== +--- src/findlib/find.h (révision 6368) ++++ src/findlib/find.h (copie de travail) +@@ -108,6 +108,7 @@ + #define FO_ENHANCEDWILD (1<<23) /* Enhanced wild card processing */ + #define FO_CHKCHANGES (1<<24) /* Check if file have been modified during backup */ + #define FO_STRIPPATH (1<<25) /* Check for stripping path */ ++#define FO_ACCURATE (1<<26) /* Accurate mode */ + + struct s_included_file { + struct s_included_file *next; diff --git a/bacula/patches/testing/project-accurate-backup.sql b/bacula/patches/testing/project-accurate-backup.sql new file mode 100644 index 0000000000..715d6f6efe --- /dev/null +++ b/bacula/patches/testing/project-accurate-backup.sql @@ -0,0 +1,83 @@ + +CREATE TABLE CurrentBackupId +( + BackupId serial not null, + ClientId integer not null, + JobName text not null, + FileSetId integer not null, + primary key (BackupId) +); + +-- Serait bien de prendre la meme table pour +-- les File et le CurrentBackup... +-- Mais y'a des problemes pour les prunes + +CREATE TABLE CurrentBackup +( + Id serial not null, + BackupId integer not null, + FullMark char(1) default 0, + PathId integer not null, + FilenameId integer not null, + MarkId integer not null default 0, + LStat text not null, + md5 text not null, + primary key (Id) +); + +CREATE INDEX currentbackup_fileid on CurrentBackup (BackupId); +CREATE INDEX currentbackup_idx on currentbackup (FilenameId, PathId); +CREATE INDEX currentbackup_idx1 on currentbackup (FilenameId); +CREATE INDEX currentbackup_idx2 on currentbackup (PathId); + + +CREATE TEMPORARY TABLE batch (path varchar, + name varchar, + lstat varchar, + md5 varchar); + +-- On batch insert dans la table temporaire + +-- il faut trouver les fichiers manquant +-- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes + +UPDATE CurrentBackup SET MarkId = 1 + WHERE CurrentBackup.Id IN ( + SELECT CurrentBackup.Id + FROM batch + JOIN Filename USING (Name) + JOIN Path USING (Path) + JOIN CurrentBackup + ON (Filename.FilenameId = CurrentBackup.FilenameId + AND + Path.PathId = CurrentBackup.PathId) + WHERE CurrentBackup.BackupId = 1 + ) + +INSERT INTO CurrentBackup (BackupId, FullMark, PathId, FilenameId, LStat, MD5) + (SELECT CurrentBackup.BackupId, 'F', Path.PathId, Filename.FilenameId, + batch.LStat, batch.MD5 + FROM batch + JOIN Path USING (Path) + JOIN Filename USING (Name) + LEFT OUTER JOIN CurrentBackup + ON (Filename.FilenameId = CurrentBackup.FilenameId + AND Path.PathId = CurrentBackup.PathId + AND CurrentBackup.BackupId = 1) + WHERE BackupId IS NULL + + ) + +-- il faut trouver les fichiers modifies +-- Le champs LStat n'est plus le meme +SELECT * + FROM batch + JOIN Path USING (Path) + JOIN Filename USING (Name) + JOIN CurrentBackup USING (FilenameId, PathId) + + WHERE CurrentBackup.LStat != batch.LStat + AND CurrentBackup.BackupId = 1 + +-- il faut mettre a jour la liste des fichiers +