static int SelectNameRegexMatch(const char *filename, char *crit) { if (FullTextMatch(crit, ReadLastNode(filename))) { return true; } return false; }
/* 'resolved' argument needs to be at least CF_BUFSIZE long */ bool ResolveFilename(const char *req_path, char *res_path) { char req_dir[CF_BUFSIZE]; char req_filename[CF_BUFSIZE]; /* * Eliminate symlinks from path, but do not resolve the file itself if it is a * symlink. */ strlcpy(req_dir, req_path, CF_BUFSIZE); ChopLastNode(req_dir); strlcpy(req_filename, ReadLastNode(req_path), CF_BUFSIZE); #if defined HAVE_REALPATH && !defined _WIN32 if (realpath(req_dir, res_path) == NULL) { return false; } #else memset(res_path, 0, CF_BUFSIZE); CompressPath(res_path, req_dir); #endif AddSlash(res_path); strlcat(res_path, req_filename, CF_BUFSIZE); /* Adjust for forward slashes */ MapName(res_path); /* NT has case-insensitive path names */ #ifdef __MINGW32__ int i; for (i = 0; i < strlen(res_path); i++) { res_path[i] = ToLower(res_path[i]); } #endif /* __MINGW32__ */ return true; }
static PromiseResult VerifyFilePromise(EvalContext *ctx, char *path, Promise *pp) { struct stat osb, oslb, dsb; Attributes a = { {0} }; CfLock thislock; int exists; a = GetFilesAttributes(ctx, pp); if (!FileSanityChecks(ctx, path, a, pp)) { return PROMISE_RESULT_NOOP; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", path, DATA_TYPE_STRING); thislock = AcquireLock(ctx, path, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_NOOP; } LoadSetuid(a); PromiseResult result = PROMISE_RESULT_NOOP; if (lstat(path, &oslb) == -1) /* Careful if the object is a link */ { if ((a.create) || (a.touch)) { if (!CfCreateFile(ctx, path, pp, a, &result)) { goto exit; } else { exists = (lstat(path, &oslb) != -1); } } exists = false; } else { if ((a.create) || (a.touch)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "File '%s' exists as promised", path); } exists = true; } if ((a.havedelete) && (!exists)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "File '%s' does not exist as promised", path); goto exit; } if (!a.havedepthsearch) /* if the search is trivial, make sure that we are in the parent dir of the leaf */ { char basedir[CF_BUFSIZE]; Log(LOG_LEVEL_DEBUG, "Direct file reference '%s', no search implied", path); snprintf(basedir, sizeof(basedir), "%s", path); if (strcmp(ReadLastNode(basedir), ".") == 0) { // Handle /. notation for deletion of directories ChopLastNode(basedir); ChopLastNode(path); } ChopLastNode(basedir); if (chdir(basedir)) { Log(LOG_LEVEL_ERR, "Failed to chdir into '%s'", basedir); } } if (exists && (!VerifyFileLeaf(ctx, path, &oslb, a, pp, &result))) { if (!S_ISDIR(oslb.st_mode)) { goto exit; } } if (stat(path, &osb) == -1) { if ((a.create) || (a.touch)) { if (!CfCreateFile(ctx, path, pp, a, &result)) { goto exit; } else { exists = true; } } else { exists = false; } } else { if (!S_ISDIR(osb.st_mode)) { if (a.havedepthsearch) { Log(LOG_LEVEL_WARNING, "depth_search (recursion) is promised for a base object '%s' that is not a directory", path); goto exit; } } exists = true; } if (a.link.link_children) { if (stat(a.link.source, &dsb) != -1) { if (!S_ISDIR(dsb.st_mode)) { Log(LOG_LEVEL_ERR, "Cannot promise to link the children of '%s' as it is not a directory!", a.link.source); goto exit; } } } /* Phase 1 - */ if (exists && ((a.havedelete) || (a.haverename) || (a.haveperms) || (a.havechange) || (a.transformer))) { lstat(path, &oslb); /* if doesn't exist have to stat again anyway */ DepthSearch(ctx, path, &oslb, 0, a, pp, oslb.st_dev, &result); /* normally searches do not include the base directory */ if (a.recursion.include_basedir) { int save_search = a.havedepthsearch; /* Handle this node specially */ a.havedepthsearch = false; DepthSearch(ctx, path, &oslb, 0, a, pp, oslb.st_dev, &result); a.havedepthsearch = save_search; } else { /* unless child nodes were repaired, set a promise kept class */ if (!IsDefinedClass(ctx, "repaired" , PromiseGetNamespace(pp))) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Basedir '%s' not promising anything", path); } } if (((a.change.report_changes) == FILE_CHANGE_REPORT_CONTENT_CHANGE) || ((a.change.report_changes) == FILE_CHANGE_REPORT_ALL)) { if (a.havedepthsearch) { PurgeHashes(ctx, NULL, a, pp); } else { PurgeHashes(ctx, path, a, pp); } } } /* Phase 2a - copying is potentially threadable if no followup actions */ if (a.havecopy) { result = PromiseResultUpdate(result, ScheduleCopyOperation(ctx, path, a, pp)); } /* Phase 2b link after copy in case need file first */ if ((a.havelink) && (a.link.link_children)) { result = PromiseResultUpdate(result, ScheduleLinkChildrenOperation(ctx, path, a.link.source, 1, a, pp)); } else if (a.havelink) { result = PromiseResultUpdate(result, ScheduleLinkOperation(ctx, path, a.link.source, a, pp)); } /* Phase 3 - content editing */ if (a.haveedit) { result = PromiseResultUpdate(result, ScheduleEditOperation(ctx, path, a, pp)); } // Once more in case a file has been created as a result of editing or copying exists = (stat(path, &osb) != -1); if (exists && (S_ISREG(osb.st_mode))) { VerifyFileLeaf(ctx, path, &osb, a, pp, &result); } if (!exists && a.havechange) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Promised to monitor '%s' for changes, but file does not exist", path); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } exit: result = PromiseResultUpdate(result, SaveSetuid(ctx, a, pp)); YieldCurrentLock(thislock); return result; }
int StatFile(ServerConnectionState *conn, char *sendbuffer, char *ofilename) /* Because we do not know the size or structure of remote datatypes,*/ /* the simplest way to transfer the data is to convert them into */ /* plain text and interpret them on the other side. */ { Stat cfst; struct stat statbuf, statlinkbuf; char linkbuf[CF_BUFSIZE], filename[CF_BUFSIZE]; int islink = false; TranslatePath(filename, ofilename); memset(&cfst, 0, sizeof(Stat)); if (strlen(ReadLastNode(filename)) > CF_MAXLINKSIZE) { snprintf(sendbuffer, CF_BUFSIZE, "BAD: Filename suspiciously long [%s]\n", filename); Log(LOG_LEVEL_ERR, "%s", sendbuffer); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } if (lstat(filename, &statbuf) == -1) { snprintf(sendbuffer, CF_BUFSIZE, "BAD: unable to stat file %s", filename); Log(LOG_LEVEL_VERBOSE, "%s. (lstat: %s)", sendbuffer, GetErrorStr()); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } cfst.cf_readlink = NULL; cfst.cf_lmode = 0; cfst.cf_nlink = CF_NOSIZE; memset(linkbuf, 0, CF_BUFSIZE); #ifndef __MINGW32__ // windows doesn't support symbolic links if (S_ISLNK(statbuf.st_mode)) { islink = true; cfst.cf_type = FILE_TYPE_LINK; /* pointless - overwritten */ cfst.cf_lmode = statbuf.st_mode & 07777; cfst.cf_nlink = statbuf.st_nlink; if (readlink(filename, linkbuf, CF_BUFSIZE - 1) == -1) { sprintf(sendbuffer, "BAD: unable to read link\n"); Log(LOG_LEVEL_ERR, "%s. (readlink: %s)", sendbuffer, GetErrorStr()); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } Log(LOG_LEVEL_DEBUG, "readlink '%s'", linkbuf); cfst.cf_readlink = linkbuf; } #endif /* !__MINGW32__ */ if ((!islink) && (stat(filename, &statbuf) == -1)) { Log(LOG_LEVEL_VERBOSE, "BAD: unable to stat file '%s'. (stat: %s)", filename, GetErrorStr()); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } Log(LOG_LEVEL_DEBUG, "Getting size of link deref '%s'", linkbuf); if (islink && (stat(filename, &statlinkbuf) != -1)) /* linktype=copy used by agent */ { statbuf.st_size = statlinkbuf.st_size; statbuf.st_mode = statlinkbuf.st_mode; statbuf.st_uid = statlinkbuf.st_uid; statbuf.st_gid = statlinkbuf.st_gid; statbuf.st_mtime = statlinkbuf.st_mtime; statbuf.st_ctime = statlinkbuf.st_ctime; } if (S_ISDIR(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_DIR; } if (S_ISREG(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_REGULAR; } if (S_ISSOCK(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_SOCK; } if (S_ISCHR(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_CHAR_; } if (S_ISBLK(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_BLOCK; } if (S_ISFIFO(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_FIFO; } cfst.cf_mode = statbuf.st_mode & 07777; cfst.cf_uid = statbuf.st_uid & 0xFFFFFFFF; cfst.cf_gid = statbuf.st_gid & 0xFFFFFFFF; cfst.cf_size = statbuf.st_size; cfst.cf_atime = statbuf.st_atime; cfst.cf_mtime = statbuf.st_mtime; cfst.cf_ctime = statbuf.st_ctime; cfst.cf_ino = statbuf.st_ino; cfst.cf_dev = statbuf.st_dev; cfst.cf_readlink = linkbuf; if (cfst.cf_nlink == CF_NOSIZE) { cfst.cf_nlink = statbuf.st_nlink; } #if !defined(__MINGW32__) if (statbuf.st_size > statbuf.st_blocks * DEV_BSIZE) #else # ifdef HAVE_ST_BLOCKS if (statbuf.st_size > statbuf.st_blocks * DEV_BSIZE) # else if (statbuf.st_size > ST_NBLOCKS(statbuf) * DEV_BSIZE) # endif #endif { cfst.cf_makeholes = 1; /* must have a hole to get checksum right */ } else { cfst.cf_makeholes = 0; } memset(sendbuffer, 0, CF_BUFSIZE); /* send as plain text */ Log(LOG_LEVEL_DEBUG, "OK: type = %d, mode = %" PRIoMAX ", lmode = %" PRIoMAX ", uid = %" PRIuMAX ", gid = %" PRIuMAX ", size = %" PRIdMAX ", atime=%" PRIdMAX ", mtime = %" PRIdMAX, cfst.cf_type, (uintmax_t)cfst.cf_mode, (uintmax_t)cfst.cf_lmode, (intmax_t)cfst.cf_uid, (intmax_t)cfst.cf_gid, (intmax_t) cfst.cf_size, (intmax_t) cfst.cf_atime, (intmax_t) cfst.cf_mtime); snprintf(sendbuffer, CF_BUFSIZE, "OK: %d %ju %ju %ju %ju %jd %jd %jd %jd %d %d %d %jd", cfst.cf_type, (uintmax_t)cfst.cf_mode, (uintmax_t)cfst.cf_lmode, (uintmax_t)cfst.cf_uid, (uintmax_t)cfst.cf_gid, (intmax_t)cfst.cf_size, (intmax_t) cfst.cf_atime, (intmax_t) cfst.cf_mtime, (intmax_t) cfst.cf_ctime, cfst.cf_makeholes, cfst.cf_ino, cfst.cf_nlink, (intmax_t) cfst.cf_dev); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); memset(sendbuffer, 0, CF_BUFSIZE); if (cfst.cf_readlink != NULL) { strcpy(sendbuffer, "OK:"); strcat(sendbuffer, cfst.cf_readlink); } else { sprintf(sendbuffer, "OK:"); } SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return 0; }