/* * Process each of the directories in the list (recursing as we go) */ static int do_dir_proc (Node *p, void *closure) { struct frame_and_entries *frent = (struct frame_and_entries *) closure; struct recursion_frame *frame = frent->frame; struct recursion_frame xframe; char *dir = p->key; char *newrepos, *virtrepos; List *sdirlist; char *srepository; char *smapped_repository; Dtype dir_return = R_PROCESS; Dtype hint; int stripped_dot = 0; int err = 0; struct saved_cwd cwd; char *saved_update_dir; int process_this_directory = 1; int directory_opened = 0; char *old_update_repos = NULL; int directory_not_valid = 0; if (fncmp (dir, CVSADM) == 0 || fncmp (dir, CVSDUMMY) == 0) { /* This seems to most often happen when users (beginning users, generally), try "cvs ci *" or something similar. On that theory, it is possible that we should just silently skip the CVSADM directories, but on the other hand, using a wildcard like this isn't necessarily a practice to encourage (it operates only on files which exist in the working directory, unlike regular CVS recursion). */ /* FIXME-reentrancy: printed_cvs_msg should be in a "command struct" or some such, so that it gets cleared for each new command (this is possible using the remote protocol and a custom-written client). The struct recursion_frame is not far back enough though, some commands (commit at least) will call start_recursion several times. An alternate solution would be to take this whole check and move it to a new function validate_arguments or some such that all the commands call and which snips the offending directory from the argc,argv vector. */ static int printed_cvs_msg = 0; if (!printed_cvs_msg) { error (0, 0, "warning: directory %s specified in argument", dir); error (0, 0, "but CVS uses %s for its own purposes; skipping %s directory", dir, dir); printed_cvs_msg = 1; } return 0; } saved_update_dir = update_dir; update_dir = (char*)xmalloc (strlen (saved_update_dir) + strlen (dir) + 5); strcpy (update_dir, saved_update_dir); /* set up update_dir - skip dots if not at start */ if (strcmp (dir, ".") != 0) { if (update_dir[0] != '\0') { (void) strcat (update_dir, "/"); (void) strcat (update_dir, dir); } else (void) strcpy (update_dir, dir); /* * Here we need a plausible repository name for the sub-directory. We * create one by concatenating the new directory name onto the * previous repository name. The only case where the name should be * used is in the case where we are creating a new sub-directory for * update -d and in that case the generated name will be correct. */ if (repository == NULL) { newrepos = xstrdup (""); virtrepos = xstrdup (""); } else { if(frame->which&W_LOCAL) { char *d=(char*)xmalloc(strlen(dir)+strlen(CVSADM_REP)+32); sprintf(d,"%s/%s",dir,CVSADM_REP); if(isfile(d)) virtrepos = Name_Repository(dir,update_dir); else { virtrepos = (char*)xmalloc (strlen (repository) + strlen (dir) + 5); sprintf (virtrepos, "%s/%s", repository, dir); } xfree(d); } else { virtrepos = (char*)xmalloc (strlen (repository) + strlen (dir) + 5); sprintf (virtrepos, "%s/%s", repository, dir); } if(!current_parsed_root->isremote) { newrepos = map_repository(virtrepos); if(!newrepos) error(1,0,"Internal error - couldn't map %s to anything",virtrepos); } else newrepos = xstrdup(virtrepos); } } else { if (update_dir[0] == '\0') (void) strcpy (update_dir, dir); if (repository == NULL) { newrepos = xstrdup (""); virtrepos = xstrdup (""); } else { newrepos = xstrdup (mapped_repository); virtrepos = xstrdup (repository); } } /* Check to see that the CVSADM directory, if it exists, seems to be well-formed. It can be missing files if the user hit ^C in the middle of a previous run. We want to (a) make this a nonfatal error, and (b) make sure we print which directory has the problem. Do this before the direntproc, so that (1) the direntproc doesn't have to guess/deduce whether we will skip the directory (e.g. send_dirent_proc and whether to send the directory), and (2) so that the warm fuzzy doesn't get printed if we skip the directory. */ if (frame->which & W_LOCAL) { char *cvsadmdir; cvsadmdir = (char*)xmalloc (strlen (dir) + sizeof (CVSADM_REP) + sizeof (CVSADM_ENT) + 80); strcpy (cvsadmdir, dir); strcat (cvsadmdir, "/"); strcat (cvsadmdir, CVSADM); if (isdir (cvsadmdir)) { strcpy (cvsadmdir, dir); strcat (cvsadmdir, "/"); strcat (cvsadmdir, CVSADM_REP); if (!isfile (cvsadmdir)) { /* Some commands like update may have printed "? foo" but if we were planning to recurse, and don't on account of CVS/Repository, we want to say why. */ error (0, 0, "ignoring %s (%s missing)", update_dir, CVSADM_REP); dir_return = R_SKIP_ALL; } /* Likewise for CVS/Entries. */ if (dir_return != R_SKIP_ALL) { strcpy (cvsadmdir, dir); strcat (cvsadmdir, "/"); strcat (cvsadmdir, CVSADM_ENT); if (!isfile (cvsadmdir)) { /* Some commands like update may have printed "? foo" but if we were planning to recurse, and don't on account of CVS/Repository, we want to say why. */ error (0, 0, "ignoring %s (%s missing)", update_dir, CVSADM_ENT); dir_return = R_SKIP_ALL; } } } xfree (cvsadmdir); } /* Only process this directory if the root matches. This nearly duplicates code in do_recursion. */ /* If -d was specified, it should override CVS/Root. In the single-repository case, it is long-standing CVS behavior and makes sense - the user might want another access method, another server (which mounts the same repository), &c. In the multiple-repository case, -d overrides all CVS/Root files. That is the only plausible generalization I can think of. */ if (!(frame->which&W_FAKE) && CVSroot_cmdline == NULL && !server_active) { char *this_root = Name_Root (dir, update_dir); if (this_root != NULL) { if (findnode_fn (root_directories, this_root) == NULL) { /* Add it to our list. */ Node *n = getnode (); n->type = NT_UNKNOWN; n->key = xstrdup (this_root); if (addnode (root_directories, n)) error (1, 0, "cannot add new CVSROOT %s", this_root); } process_this_directory = (fncmp (current_parsed_root->original, this_root) == 0); xfree (this_root); } } /* * Do we have access to this directory? */ if(!current_parsed_root->isremote) { const char *tag=NULL; const char *date=NULL; int nonbranch=0; const char *message; const char *v_msg; /* before we do anything else, see if we have any per-directory tags */ ParseTag (&tag, &date, &nonbranch, NULL); if (! verify_access (frame->permproc, newrepos, NULL, update_dir, frame->tag?frame->tag:tag,&message, &v_msg)) { if(frame->permproc!=verify_read) { if(tag) error (0, 0, "User '%s' cannot %s %s on tag/branch %s", CVS_Username, v_msg, fn_root(virtrepos), tag); else error (0, 0, "User '%s' cannot %s %s", CVS_Username, v_msg, fn_root(virtrepos)); if(message) error(0,0,"%s",message); } dir_return = R_SKIP_ALL; } xfree(tag); xfree(date); } if(dir_return!=R_SKIP_ALL) { /* Generic behavior. I don't see a reason to make the caller specify a direntproc just to get this. */ if ((frame->which & (W_LOCAL|W_FAKE))) { if(!isdir (dir)) hint = R_SKIP_ALL; else hint = R_PROCESS; } else if(!isdir(newrepos)) hint = R_SKIP_ALL; else hint = R_PROCESS; } if (dir_return == R_SKIP_ALL || dir_return == R_ERROR) ; else if(process_this_directory) { int directory_opened_status=-1; const char *dirversion=NULL; const char *dirtag=NULL; const char *dirdate=NULL; int dirnonbranch=0; if(frame->predirentproc != NULL) { frame->predirentproc (frame->callerdat, dir, newrepos, update_dir, frent->entries, virtrepos, hint); } /* before we do anything else, see if we have any per-directory tags */ ParseTag_Dir (dir, &dirtag, &dirdate, &dirnonbranch, &dirversion); directory_opened_status=open_directory(newrepos,dir,dirtag,dirdate,dirnonbranch,dirversion,current_parsed_root->isremote); if (directory_opened_status!=-1) directory_opened = 1; xfree(dirversion); xfree(dirtag); xfree(dirdate); if(!current_parsed_root->isremote) fileattr_startdir(newrepos); } /* call-back dir entry proc (if any) */ if (dir_return == R_SKIP_ALL || dir_return == R_ERROR) ; else if (frame->direntproc != NULL) { /* If we're doing the actual processing, call direntproc. Otherwise, assume that we need to process this directory and recurse. FIXME. */ if (process_this_directory) dir_return = frame->direntproc (frame->callerdat, dir, newrepos, update_dir, frent->entries, virtrepos, hint); else dir_return = R_PROCESS; } else { /* Generic behavior. I don't see a reason to make the caller specify a direntproc just to get this. */ dir_return = hint; } /* only process the dir if the return code was 0 */ if (dir_return != R_SKIP_ALL && dir_return !=R_ERROR) { /* save our current directory and static vars */ if (save_cwd (&cwd)) error_exit (); sdirlist = dirlist; srepository = repository; smapped_repository = mapped_repository; dirlist = NULL; /* cd to the sub-directory */ if (!(frame->which&(W_LOCAL|W_FAKE))) { if ( CVS_CHDIR (newrepos) < 0) { error (1, errno, "could not chdir to %s", fn_root(newrepos)); } } else { if ( !(frame->which&W_FAKE) && CVS_CHDIR (dir) < 0) { if(!noexec) error (1, errno, "could not chdir to %s", fn_root(update_dir)); else directory_not_valid=1; } } old_update_repos = update_repos; update_repos = xstrdup(newrepos); /* honor the global SKIP_DIRS (a.k.a. local) */ if (frame->flags == R_SKIP_DIRS) dir_return = R_SKIP_DIRS; /* remember if the `.' will be stripped for subsequent dirs */ if (strcmp (update_dir, ".") == 0) { update_dir[0] = '\0'; stripped_dot = 1; } /* make the recursive call */ xframe = *frame; xframe.flags = dir_return; if(directory_not_valid) { xframe.which &= ~W_LOCAL; xframe.which |= W_FAKE; } err += do_recursion (&xframe, 0); /* put the `.' back if necessary */ if (stripped_dot) (void) strcpy (update_dir, "."); /* call-back dir leave proc (if any) */ if (process_this_directory && frame->dirleaveproc != NULL) err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir, frent->entries); if(directory_opened) close_directory(); /* get back to where we started and restore state vars */ if (restore_cwd (&cwd, NULL)) error_exit (); xfree(update_repos); update_repos = old_update_repos; free_cwd (&cwd); dirlist = sdirlist; repository = srepository; mapped_repository = smapped_repository; } else { if(directory_opened) close_directory(); fileattr_write(); fileattr_free(); } xfree (newrepos); xfree (virtrepos); xfree (update_dir); update_dir = saved_update_dir; if(dir_return == R_ERROR) err++; return (err); }
static int rlsacl_proc(int argc, char **argv, const char *xwhere, const char *mwhere, const char *mfile, int shorten, int local_specified, const char *mname, const char *msg) { /* Begin section which is identical to patch_proc--should this be abstracted out somehow? */ char *myargv[2]; int err = 0; char *repository, *mapped_repository; char *where; repository = (char*)xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); where = (char*)xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1) + 1); (void) strcpy (where, argv[0]); /* if mfile isn't null, we need to set up to do only part of the module */ if (mfile != NULL) { char *cp; char *path; /* if the portion of the module is a path, put the dir part on repos */ if ((cp = (char*)strrchr (mfile, '/')) != NULL) { *cp = '\0'; (void) strcat (repository, "/"); (void) strcat (repository, mfile); (void) strcat (where, "/"); (void) strcat (where, mfile); mfile = cp + 1; } /* take care of the rest */ path = (char*)xmalloc (strlen (repository) + strlen (mfile) + 5); (void) sprintf (path, "%s/%s", repository, mfile); if (isdir (path)) { /* directory means repository gets the dir tacked on */ (void) strcpy (repository, path); (void) strcat (where, "/"); (void) strcat (where, mfile); } else { myargv[0] = argv[0]; myargv[1] = (char*)mfile; argc = 2; argv = myargv; } xfree (path); } mapped_repository = map_repository(repository); /* cd to the starting repository */ if ( CVS_CHDIR (mapped_repository) < 0) { error (0, errno, "cannot chdir to %s", fn_root(repository)); xfree (repository); xfree (mapped_repository); return (1); } xfree (repository); /* End section which is identical to patch_proc. */ err = start_recursion (lsacl_fileproc, (FILESDONEPROC) NULL, (PREDIRENTPROC) NULL, lsacl_dirproc, (DIRLEAVEPROC) NULL, NULL, argc - 1, argv + 1, local_specified, W_REPOS, 0, 1, where, mapped_repository, 1, NULL); xfree (mapped_repository); return err; }
/* * Implement the recursive policies on the local directory. This may be * called directly, or may be called by start_recursion */ static int do_recursion (struct recursion_frame *frame, int top_level) { int err = 0; int dodoneproc = 1; char *srepository; List *entries = NULL; int should_readlock; int process_this_directory = 1; /* do nothing if told */ if (frame->flags == R_SKIP_ALL) return (0); should_readlock = noexec ? 0 : frame->readlock; /* Check the value in CVSADM_ROOT and see if it's in the list. If not, add it to our lists of CVS/Root directories and do not process the files in this directory. Otherwise, continue as usual. THIS_ROOT might be NULL if we're doing an initial checkout -- check before using it. The default should be that we process a directory's contents and only skip those contents if a CVS/Root file exists. If we're running the server, we want to process all directories, since we're guaranteed to have only one CVSROOT -- our own. */ /* If -d was specified, it should override CVS/Root. In the single-repository case, it is long-standing CVS behavior and makes sense - the user might want another access method, another server (which mounts the same repository), &c. In the multiple-repository case, -d overrides all CVS/Root files. That is the only plausible generalization I can think of. */ if(!(frame->which&W_FAKE) && CVSroot_cmdline == NULL && ! server_active) { char *this_root = Name_Root ((char *) NULL, update_dir); if (this_root != NULL) { if (findnode_fn (root_directories, this_root) == NULL) { /* Add it to our list. */ Node *n = getnode (); n->type = NT_UNKNOWN; n->key = xstrdup (this_root); if (addnode (root_directories, n)) error (1, 0, "cannot add new CVSROOT %s", this_root); } process_this_directory = (fncmp (current_parsed_root->original, this_root) == 0); xfree (this_root); } } /* * Fill in repository with the current repository */ if (frame->which & W_LOCAL) { if (isdir (CVSADM)) repository = Name_Repository ((char *) NULL, update_dir); else repository = NULL; } else { if(update_repos) /* May have been preloaded by checkout */ repository = xstrdup(update_repos); else { repository = (char*)xmalloc(strlen(current_parsed_root->directory)+strlen(update_dir)+10); sprintf(repository,"%s/%s",current_parsed_root->directory,update_dir); } } if(repository && ISDIRSEP(repository[strlen(repository)-1])) repository[strlen(repository)-1]='\0'; srepository = repository; /* remember what to free */ if(repository && !current_parsed_root->isremote) { mapped_repository = map_repository(repository); } else { mapped_repository=xstrdup(repository); } xfree(last_repository); last_repository = xstrdup(mapped_repository); /* * Do we have access to this directory? */ /* Note that for a recursion this is done already in do_dir_proc... */ if(top_level) { if(repository && !current_parsed_root->isremote) { const char *tag; const char *message; const char *v_msg; ParseTag(&tag, NULL, NULL, NULL); if (! verify_access(frame->permproc, mapped_repository, NULL, update_dir,frame->tag?frame->tag:tag,&message, &v_msg)) { if(tag) error (0, 0, "User '%s' cannot %s %s on tag/branch %s", CVS_Username, v_msg, fn_root(repository), tag); else error (0, 0, "User '%s' cannot %s %s", CVS_Username, v_msg, fn_root(repository)); if(message) error (0, 0, "%s", message); return (1); } xfree(tag); fileattr_startdir (mapped_repository); } } /* * The filesdoneproc needs to be called for each directory where files * processed, or each directory that is processed by a call where no * directories were passed in. In fact, the only time we don't want to * call back the filesdoneproc is when we are processing directories that * were passed in on the command line (or in the special case of `.' when * we were called with no args */ if (dirlist != NULL && filelist == NULL) dodoneproc = 0; /* * If filelist or dirlist is already set, we don't look again. Otherwise, * find the files and directories */ if (filelist == NULL && dirlist == NULL) { /* both lists were NULL, so start from scratch */ if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) { int lwhich = frame->which; /* In the !(which & W_LOCAL) case, we filled in repository earlier in the function. In the (which & W_LOCAL) case, the Find_Names function is going to look through the Entries file. If we do not have a repository, that does not make sense, so we insist upon having a repository at this point. Name_Repository will give a reasonable error message. */ if (repository == NULL) repository = Name_Repository ((char *) NULL, update_dir); else if(mapped_repository == NULL) mapped_repository = map_repository(repository); /* find the files and fill in entries if appropriate */ if (process_this_directory) { filelist = Find_Names (mapped_repository, lwhich, frame->aflag, &entries, repository); if (filelist == NULL) { error (0, 0, "skipping directory %s", update_dir); /* Note that Find_Directories and the filesdoneproc in particular would do bad things ("? foo.c" in the case of some filesdoneproc's). */ goto skip_directory; } } } if (frame->flags == R_SKIP_DIRS && !(frame->which&W_LOCAL) && nonrecursive_module(repository)) frame->flags = R_SKIP_DIRS; /* find sub-directories if we will recurse */ if (frame->flags != R_SKIP_DIRS) dirlist = Find_Directories ( process_this_directory ? mapped_repository : NULL, frame->which, entries, repository); } else { /* something was passed on the command line */ if (filelist != NULL && frame->fileproc != NULL) { /* we will process files, so pre-parse entries */ if (frame->which & W_LOCAL) entries = Entries_Open (frame->aflag, NULL); } } /* process the files (if any) */ if (process_this_directory && filelist != NULL && frame->fileproc) { struct file_info finfo_struct; struct frame_and_file frfile; /* read lock it if necessary */ if (should_readlock && mapped_repository && Reader_Lock (mapped_repository) != 0) error (1, 0, "read lock failed - giving up"); /* For the server, we handle notifications in a completely different place (server_notify). For local, we can't do them here--we don't have writelocks in place, and there is no way to get writelocks here. */ if (current_parsed_root->isremote) client_notify_check (repository, update_dir); finfo_struct.repository = mapped_repository; finfo_struct.update_dir = update_dir; finfo_struct.entries = entries; finfo_struct.virtual_repository = repository; /* do_file_proc will fill in finfo_struct.file. */ frfile.finfo = &finfo_struct; frfile.frame = frame; /* process the files */ err += walklist (filelist, do_file_proc, &frfile); /* unlock it */ if (should_readlock) Lock_Cleanup_Directory(); /* clean up */ if (filelist) dellist (&filelist); } /* call-back files done proc (if any) */ if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL) { err = frame->filesdoneproc (frame->callerdat, err, (char*)mapped_repository, (char*)(update_dir[0] ? update_dir : "."), entries); } skip_directory: if(repository && !current_parsed_root->isremote) { fileattr_write (); fileattr_free (); } /* process the directories (if necessary) */ if (dirlist != NULL) { // BUGME!!! /* for some reason this code path is not entered some times when it SHOULD be, eg: after a rename! This means the .direcrory_history file is not created !!!! */ struct frame_and_entries frent; frent.frame = frame; frent.entries = entries; err += walklist (dirlist, do_dir_proc, (void *) &frent); } if (dirlist) dellist (&dirlist); if (entries) { Entries_Close (entries); entries = NULL; } /* free the saved copy of the pointer if necessary */ xfree (srepository); xfree (mapped_repository); repository = NULL; return (err); }