/* gsubmit.c Predecessor: */ /* ------------------------------- */ /* Submit v1.0 */ /* ------------------------------- */ /* (c) 1994 Brad Drysdale */ /* George Kar-Tung Chong */ /* ------------------------------- */ /* Date of Origin: 07-07-94 */ /* ------------------------------- */ /* First rewritten: Peter Gacs, 02-20-97 Latest modification: Eric Missimer, 12-02-17 (replacing sprintf with snprintf). Summary of changes compared to submit: - Some bugs killed. - More structured program. - The student can submit a directory. -cp option. - The instructor can specify what filenames are submittable. Methods used: - A setuid program. - system(...) is only used on generated filenames. */ #include #include #include #include #include #include #include #include #include #include #include #define str_endof(x) (x + strlen(x)) #define BASE "/cs/course" #define HOMEWORK "current/homework" /* in BASE/[course number] */ #define SPOOL "spool" /* Spooling destination, in HOMEWORK */ #define LOG "log" /* Logfile destination, in HOMEWORK */ #define MASK 0700 /* Mask out permissions */ /* Directory creation return constants */ #define CREATED 2 #define EXISTS 1 #define FAIL (-1) #define SUCC 1 #define EXIT_FAILURE 1 /* This should not be needed! Apparently -ansi erases its definition from stdlib.h */ char *mktemp(char *); char *progname; /* argv[0], for error messages */ /* These could be local, but I am lazy to redeclare them in every function. */ char msg[BUFSIZ]; /* error messages */ char action[BUFSIZ]; /* action to be logged */ char *course; /* argv[1] */ char course_hw_dir[BUFSIZ]; /* course homework directory */ struct stat cstat; /* dir's stat */ char student_spool[BUFSIZ]; /* student's submission directory */ struct passwd pwd; /* student's password structure */ void usage( void ) { fprintf( stderr, "Usage for %s: \n", progname ); fprintf( stderr, "\t%s course [-]file1 [[-]file2 ... ]\n", progname ); fprintf( stderr, "The '-' prefix causes that file to be " ); fprintf( stderr, "unsubmitted\n" ); fprintf( stderr, "\t%s course -ls (Show all submitted files)\n", progname ); fprintf( stderr, "\t%s course -cat file1 [file2 ... ]\n", progname ); fprintf( stderr, "Print submitted file(s) on the standard output\n" ); fprintf( stderr, "\t%s course [-rm|--] [file1 ...]\n", progname ); fprintf( stderr, "Remove all submissions\n" ); fprintf( stderr, "\t%s course -cp file1 [file2 ...] [targdir]\n", progname ); fprintf( stderr, "Submit a file or directory, into the given target\n"); fprintf( stderr, "subdirectory of the user's submission directory\n" ); fprintf( stderr, "(default target is the submission directory itself)\n" ); fprintf( stderr, "\tman %s (Read the man page)\n", progname ); exit(EXIT_FAILURE); } char *endof_pathname( char *pathname ) /* Returns the last part of a pathname. An ending '/' is ignored. */ { char *filename; if ( (filename = strrchr( pathname, '/' )) ) filename++; else filename = pathname; return filename; } void delete_ending_slash( char *file ) /* delete possible ending '/' */ { int n; n = strlen(file); if ('/' == file[n - 1]) file[n - 1] = '\0'; } int isdir( char *file ) { struct stat sbuf; mode_t fmt; if ( lstat( file, &sbuf ) ) { snprintf( msg, BUFSIZ, "isdir(%s)\n", file); perror(msg); return FAIL; } fmt = sbuf.st_mode & S_IFMT; return((fmt == S_IFDIR) ? 1 : 0); } int islnk( char *file ) { struct stat sbuf; mode_t fmt; if ( lstat( file, &sbuf ) ) { snprintf( msg, BUFSIZ, "%s\n", file); perror(msg); return(-1); } fmt = sbuf.st_mode & S_IFMT; return((fmt == S_IFLNK) ? 1 : 0); } void user_privi(void) /* Gives access to the user's directories */ { if (setuid( 0 )) { snprintf( msg, BUFSIZ, "setuid(0)" ); perror(msg); exit(EXIT_FAILURE); } if ( seteuid( pwd.pw_uid ) ) { snprintf( msg, BUFSIZ, "Can't run as user %s\n", getpwuid( pwd.pw_uid )->pw_name ); perror(msg); exit(EXIT_FAILURE); } } void instructor_privi(void) /* Gives access to the course directories */ { if (setuid( 0 )) { snprintf( msg, BUFSIZ, "setuid(0)" ); perror(msg); exit(EXIT_FAILURE); } if ( setegid( cstat.st_gid ) ) { snprintf( msg, BUFSIZ, "Can't run as group %s\n", getgrgid( cstat.st_gid )->gr_name ); perror(msg); exit(EXIT_FAILURE); } if ( seteuid( cstat.st_uid ) ) { snprintf( msg, BUFSIZ, "Can't run as user %s\n", getpwuid( cstat.st_uid )->pw_name ); perror(msg); exit(EXIT_FAILURE); } } void local_root_privi(void) /* Enables to change the owner of the temp dir */ { if (setuid( 0 )) { snprintf( msg, BUFSIZ, "setuid(0)" ); perror(msg); exit(EXIT_FAILURE); } } mode_t compute_mode( char *file ) /* Returns: the mode of the submitted file, or 0 if it fails. */ { struct stat sbuf; mode_t new_mode; if ( stat( file, &sbuf ) ) { snprintf( msg, BUFSIZ, "Can't read file %s, or it does not exist", file ); perror(msg); return 0; } new_mode = sbuf.st_mode & MASK; new_mode |= ( new_mode >> 3 ); return new_mode; } int confirm( char *file ) /* Returns 1 if the file deletion is confirmed, 0 otherwise. */ { char Answer[5]; fprintf( stderr, "Delete file %s (y/n)? ", endof_pathname(file) ); scanf( "%1s", Answer ); return (!strcmp(Answer, "y") || !strcmp(Answer, "Y")) ? 1 : 0; } int make_dir_maybe( char *dir_name, char *dir_type ) /* Returns: EXISTS if the directory already exists, CREATED if the creation succeeded, FAIL otherwise. */ { struct stat statp; instructor_privi(); if ( !stat( dir_name, &statp ) ) return EXISTS; if ( mkdir( dir_name, 02770 ) ) { snprintf( msg, BUFSIZ, "Failed to make %s directory.\n", dir_type ); perror(msg); return FAIL; } if ( chown( dir_name, cstat.st_uid, cstat.st_gid) ) { snprintf( msg, BUFSIZ, "Failed to change ownership of %s directory.\n", dir_type ); perror(msg); return FAIL; } if ( chmod( dir_name, 02770 ) ) { snprintf( msg, BUFSIZ, "Failed to set permissions on %s directory.\n", dir_type ); perror(msg); return FAIL; } return CREATED; } int create_logfile_maybe( char *logname ) /* Creates the logfile unless it already exists. Aborts the program if it fails. Returns 2 if it created the logfile now, 1 otherwise. Aborts the program if t he creation fails. */ { FILE *fd = (FILE *)0; time_t timed; struct stat statfile; instructor_privi(); snprintf( logname, BUFSIZ, "%s/%s", course_hw_dir, LOG ); if (FAIL == make_dir_maybe( logname, "log" )) { fprintf(stderr, "Failed to create log directory"); exit(EXIT_FAILURE); } /* Check for errors */ snprintf( str_endof(logname), BUFSIZ, "/%s", pwd.pw_name ); if ( !stat( logname, &statfile ) ) return EXISTS; if ( !(fd = fopen( logname, "w" )) ) { snprintf( msg, BUFSIZ, "Can't create log file %s\n", logname); perror(msg); exit(EXIT_FAILURE); } timed = time( (time_t *) 0 ); fprintf( fd, "==================================================================\n" ); fprintf( fd, "Submission Logfile for %s (%s)\nCreated at %s", pwd.pw_name, pwd.pw_gecos, ctime( &timed )); fprintf( fd, "==================================================================\n\n" ); (void)fflush( fd ); (void)fclose( fd ); if ( chown( logname, cstat.st_uid, cstat.st_gid ) ) { snprintf( msg, BUFSIZ, "chown( %s, , ) fails in create_logfile_maybe", logname); perror( msg ); exit(EXIT_FAILURE); } if ( chmod( logname, 0660 ) ) { snprintf( msg, BUFSIZ, "chmod( %s, )", logname); perror( msg ); exit(EXIT_FAILURE); } return CREATED; } void log_action( char *action ) /* Write a record of the action into the logfile. */ { FILE *logfile = (FILE *)0; time_t timed; char logname[BUFSIZ]; instructor_privi(); create_logfile_maybe( logname ); if ( !(logfile = fopen( logname, "a" )) ) { snprintf( msg, BUFSIZ, "Failed to open file %s\n", logname ); perror(msg); exit(EXIT_FAILURE); } timed = time( (time_t *) 0 ); fprintf( logfile, "%s %s at %s", pwd.pw_name, action, ctime( &timed ) ); fprintf( stderr, "%s %s at %s", pwd.pw_name, action, ctime( &timed ) ); (void)fflush( logfile ); (void)fclose( logfile ); } int has_dot_dot( char *pathname ) /* .. is not allowed in filenames in order to confine the user to subdirectories of the student_spool directory. Returns 0 if .. does not occur, 1 if it does. */ { if ( strstr( pathname, ".." ) ) { snprintf( action, BUFSIZ, "illegal pathname %s containing \"..\"", pathname ); log_action( action ); return 1; } return 0; } void create_spool_maybe( ) { /* If student_spool directory does not exist then create it and change ownership to the course coordinator who owns the directory above. */ struct stat sstat; instructor_privi(); snprintf( student_spool, BUFSIZ, "%s/%s", course_hw_dir, SPOOL ); if (! make_dir_maybe( student_spool, "spool" )) { fprintf(stderr, "Failed to make spool directory)"); exit(EXIT_FAILURE); } snprintf( str_endof(student_spool), BUFSIZ, "/%s", pwd.pw_name ); if ( !stat( student_spool, &sstat ) ) return; if ( mkdir( student_spool, 02770 ) ) { snprintf( msg, BUFSIZ, "Failed to make directory %s.\n", student_spool ); perror(msg); exit(EXIT_FAILURE); } if ( chown( student_spool, cstat.st_uid, cstat.st_gid) ) { snprintf( msg, BUFSIZ, "Failed to change ownership of directory %s.\n", student_spool ); perror(msg); exit(EXIT_FAILURE); } if ( chmod( student_spool, 02770 ) ) { snprintf( msg, BUFSIZ, "Failed to set permissions on directory %s.\n", student_spool ); perror(msg); exit(EXIT_FAILURE); } } void create_header_maybe( ) { FILE *fd = (FILE *)0; char real_name[BUFSIZ], pathname[BUFSIZ], buid[BUFSIZ], lbuf[BUFSIZ], check[2]; struct stat statfile; time_t timed; int i; long buin; instructor_privi(); snprintf( pathname, BUFSIZ, "%s/HEADER", student_spool); if ( !stat( pathname, &statfile) ) return; if ( !(fd = fopen( pathname, "w" )) ) { snprintf( msg, BUFSIZ, "Could not create header file %s\n", pathname ); perror(msg); exit(EXIT_FAILURE); } for( i=0; i < 20; i++ ){ fprintf( stderr, "\nEnter your real name: " ); fgets( real_name, BUFSIZ, stdin); fprintf( stderr, "\nEnter your university ID "); fprintf( stderr, "(as 9 characters, e.g. U32551690. \n" ); fprintf( stderr, "Type 0 if you have no ID"); fprintf( stderr, " (a random ID will be assigned): " ); fgets( buid, BUFSIZ, stdin); if (10 != strlen(buid) && strcmp("0\n", buid)) { /* Since fgets also reads the newline character */ fprintf( stderr, "Illegal format for ID.\n" ); continue; } buid[9] = '\0'; fprintf( stderr, "You have entered:\n" ); fprintf( stderr, "name: %s\n", real_name ); fprintf( stderr, "login name: %s\n", pwd.pw_name ); fprintf( stderr, "ID: %s\n", buid ); fprintf( stderr, "Is this correct? (y/n) " ); fgets( lbuf, BUFSIZ, stdin); sscanf( lbuf, "%1s", check ); if ( 'y' == check[0] ) break; } if ( i == 20 ) { fprintf( stderr, "Sorry.\n" ); exit(EXIT_FAILURE); } if ( 9 != strlen(buid) ) { buin = (long) time( (time_t *) 0 ); buin = buin % 1000000000; /* We only want the last digits */ snprintf(buid, BUFSIZ, "%9ld", buin); fprintf( stderr, "\nI assigned to you ID %s.\n", buid ); fprintf( stderr, "If you forget it, type %s %s -cat HEADER\n", progname, course ); } fprintf( stderr, "\nIf you want to change your personal data later, " ); fprintf( stderr, "type \n%s %s -rm HEADER\n", progname, course ); fprintf( stderr, "%s %s -ls\n", progname, course ); fprintf( fd, "#######################################################\n" ); fprintf( fd, "## ##\n" ); fprintf( fd, "## Real Name : %s\n", real_name ); fprintf( fd, "## Login Name : %s\n", pwd.pw_name ); fprintf( fd, "## ID : %s\n", buid ); timed = time( (time_t *) 0 ); fprintf( fd, "## Created At : %s", ctime( &timed ) ); fprintf( fd, "## ##\n" ); fprintf( fd, "#######################################################\n" ); (void)fflush( fd ); (void)fclose( fd ); if ( chown( pathname, cstat.st_uid, cstat.st_gid ) ) { snprintf( msg, BUFSIZ, "chown(%s, , ) failed in create_header_maybe", pathname ); perror( msg ); } if ( chmod( pathname, 0440 ) ) { snprintf( msg, BUFSIZ, "chmod(%s, ) failed in create_header_maybe", pathname ); perror( msg ); } fprintf( stderr, "\n" ); } int display_dir( char *dirname, char *disp_prefix, FILE *fp ) /* Displays directory dirname recursively. disp_prefix is the indentation string before listing. Returns: SUCC if it succeeds, FAIL otherwise. */ { DIR *dirp; struct dirent *dp; char lbuf[BUFSIZ]; char new_disp_prefix[BUFSIZ]; int isdir_return, islnk_return; int i = 0; instructor_privi(); if ( !(dirp = opendir( dirname )) ) { snprintf( msg, BUFSIZ, "Could not open directory %s", dirname); perror( msg ); return FAIL; } while( (dp = readdir( dirp )) ) { if ( !strcmp( dp->d_name, "HEADER") ) continue; snprintf( lbuf, BUFSIZ, "%s/%s", dirname, dp->d_name ); if ( FAIL == (isdir_return = isdir(lbuf)) ) { snprintf( msg, BUFSIZ, "isdir(%s)", lbuf); perror( msg ); return FAIL; } if ( ! isdir_return ) { i++; if ( 0 > (islnk_return = islnk(lbuf)) ) { snprintf( msg, BUFSIZ, "islnk(%s)", lbuf); perror( msg ); return FAIL; } if ( islnk_return ) fprintf( fp, "%s%s@\n", disp_prefix, dp->d_name ); else fprintf( fp, "%s%s\n", disp_prefix, dp->d_name ); } else if ( strcmp( dp->d_name, ".") && strcmp( dp->d_name, "..")) { fprintf( fp, "%s%s/\n", disp_prefix, dp->d_name); snprintf(new_disp_prefix, BUFSIZ, "%s\t", disp_prefix); display_dir( lbuf, new_disp_prefix, stdout ); } } /* if ( !i ) fprintf( stderr, "%sNo non-directory files in %s\n", disp_prefix, endof_pathname( dirname ) ); */ return SUCC; } int remove_file( char *file ) /* Returns: SUCC if it succeeds, FAIL if it fails. Recursive. */ { char lbuf[BUFSIZ]; int isdir_return; DIR *dirp; struct dirent *dp; struct stat statfile; instructor_privi(); if (! confirm( file )) return FAIL; if (has_dot_dot( file )) return FAIL; if ( 0 > lstat( file, &statfile ) ) { snprintf( action, BUFSIZ, "tried to remove nonexistent file %s", file ); log_action( action ); return FAIL; } if ( strstr( file, "GRADE" ) ) { snprintf( action, BUFSIZ, "tried to remove grade file" ); log_action( action ); return FAIL; } if ( FAIL == ( isdir_return = isdir( file )) ) { fprintf( stderr , "isdir(%s) fails\n", file ); return FAIL; } if ( ! isdir_return ) { if ( 0 > unlink( file ) ) { snprintf( msg, BUFSIZ, "unlink( %s ) fails", file); perror(msg); return FAIL; } } else { if ( NULL == (dirp = opendir( file )) ) { snprintf( msg, BUFSIZ, "Could not open directory %s", file ); perror( msg ); return FAIL; } while( (dp = readdir( dirp )) ) { snprintf( lbuf, BUFSIZ, "%s/%s", file, dp->d_name ); if ( strcmp( dp->d_name, ".") && strcmp( dp->d_name, "..") ) if (FAIL == remove_file( lbuf )) return FAIL; } if ( 0 > rmdir( file ) ) { snprintf( msg, BUFSIZ, "rmdir( %s ) fails", file); perror(msg); return FAIL; } } snprintf( action, BUFSIZ, "removed %s", endof_pathname(file) ); log_action( action ); return SUCC; } void unsubmit_files(int argc, char *argv[]) { int i; DIR *dirp; struct dirent *dp; char lbuf[BUFSIZ]; instructor_privi(); if ( 3 < argc ) { for ( i = 3; i < argc; i++ ) { snprintf( lbuf, BUFSIZ, "%s/%s", student_spool, argv[i] ); remove_file( lbuf ); } return; } if ( !(dirp = opendir( student_spool ))) { snprintf( msg, BUFSIZ, "Could not open directory %s", student_spool ); perror( msg ); exit(EXIT_FAILURE); } while( (dp = readdir( dirp )) ) { snprintf( lbuf, BUFSIZ, "%s/%s", student_spool, dp->d_name ); if ( strcmp( dp->d_name, ".") && strcmp( dp->d_name, "..") && strcmp( dp->d_name, "HEADER" ) ) remove_file( lbuf ); } snprintf( action, BUFSIZ, "removed all submissions"); log_action( action ); } int copy_file_fd(char *source, int fdd) /* Returns: SUCC if it succeeds, FAIL otherwise. */ { ssize_t bytes; char buffer[BUFSIZ]; int fds; if(FAIL == (fds = open(source, O_RDONLY))) { snprintf(msg, BUFSIZ, "failed to open %s.", source); perror(msg); return FAIL; } while(0< (bytes = read(fds, buffer, BUFSIZ))) write(fdd, buffer, bytes); close(fds); return SUCC; } int copy_fd_file(int fds, char *dest) /* Returns: SUCC if it succeeds, FAIL otherwise. */ { ssize_t bytes; char buffer[BUFSIZ]; int fdd; if(FAIL == (fdd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0644))) { snprintf(msg, BUFSIZ, "failed to open %s.", dest); perror(msg); return FAIL; } while(0< (bytes = read(fds, buffer, BUFSIZ))) write(fdd, buffer, bytes); close(fdd); return SUCC; } int display_file(char *source) /* Returns: SUCC if it succeeds, FAIL otherwise. */ { ssize_t bytes; int fd; char buffer[BUFSIZ]; if(FAIL == (fd = open(source, O_RDONLY))) { snprintf( msg, BUFSIZ, "open(%s)", source ); perror(msg); return FAIL; } while(0< (bytes = read(fd, buffer, BUFSIZ))) write(1, buffer, bytes); close(fd); return SUCC; } int show_file(char *file) /* Returns: SUCC if it succeeds, FAIL otherwise. */ { char pathname[BUFSIZ]; struct stat sbuf; instructor_privi(); if (has_dot_dot( file )) return FAIL; snprintf(pathname, BUFSIZ, "%s/%s", student_spool, file); if ( 0 > lstat( pathname, &sbuf ) ) { snprintf( action, BUFSIZ, "looked at nonexistent file %s", file); log_action( action ); return FAIL; } if ( FAIL == isdir( pathname ) ) { fprintf( stderr, "isdir(%s) fails", file); return FAIL; } if ( isdir( pathname ) ) { snprintf( action, BUFSIZ, "tried to look at directory %s", file); log_action( action ); return FAIL; } if (FAIL == display_file(pathname)){ snprintf(msg, BUFSIZ, "failure to show %s", pathname); perror(msg); return FAIL; } snprintf( action, BUFSIZ, "looked at file %s", file); log_action( action ); return SUCC; } int ck_submittable( char *file ) /* ARG: file to be submitted. RET: FAIL if course_hw_dir/submittable exists but course_hw_dir/submittable/file does not; SUCC otherwise. SIDE: If it returns FAIL it also prints an error message showing all submittable files. */ { struct stat sbuf; char submittable_dir[BUFSIZ]; char to_submit[BUFSIZ]; snprintf( submittable_dir, BUFSIZ, "%s/%s", course_hw_dir, "submittable" ) ; if ( FAIL == lstat( submittable_dir, &sbuf ) ) { return SUCC; } snprintf( to_submit, BUFSIZ, "%s/%s", submittable_dir, file ) ; if ( FAIL == lstat( to_submit, &sbuf ) ) { fprintf(stderr, "Path %s is not submittable.\n", file); fprintf(stderr, "Submittable files:\n"); display_dir( submittable_dir, " ", stderr); return FAIL; } return SUCC; } int submit_file( char *from_file, char *targ_dir ) /* ARGS: from_file can also be a directory. targ_dir is relative to the student_spool directory. PRECOND: if course_hw_dir/submittable is defined then each file name must match a pathname starting in this subdirectory, otherwise an error message will be printed. RET: SUCC if it succeeds, FAIL otherwise. REM: Symbolic links will be skipped. Hard links will be duplicated. */ { char from_tmp[BUFSIZ], from_path[BUFSIZ], targ_dir_path[BUFSIZ], targ_path[BUFSIZ], cwd[BUFSIZ], from_file1[BUFSIZ], targ_dir1[BUFSIZ], endof_from_file[BUFSIZ], full_subm_path[BUFSIZ]; struct stat statfile; int isdir_return, islnk_return; mode_t mode; DIR *dirp; struct dirent *dp; int temp_fd; user_privi(); strcpy( endof_from_file, endof_pathname(from_file) ); snprintf( full_subm_path, BUFSIZ, "%s/%s", targ_dir, endof_from_file ); /* Check for various impermissible submitted files. */ if (FAIL == ck_submittable( full_subm_path )) return FAIL; if (has_dot_dot( from_file )) return FAIL; if ('.' == endof_from_file[0]){ fprintf( stderr, "Filename %s begins with dot, not submitted.", from_file ); return FAIL; } if ( 0 > lstat( from_file, &statfile ) ) { snprintf(action, BUFSIZ, "tried to submit nonexistent file %s", from_file); log_action( action ); return FAIL; } if ( 0 > (islnk_return = islnk( from_file )) ) { snprintf( msg, BUFSIZ, "islnk(%s) in submit_file", from_file ); perror(msg); return FAIL; } else if ( islnk_return ) { snprintf( msg, BUFSIZ, "tried to copy symbolic link %s", from_file ); fprintf(stderr, "%s\n", msg ); return FAIL; } if ( statfile.st_uid != pwd.pw_uid ) { snprintf(action, BUFSIZ, "tried to submit not own file %s", from_file ); log_action( action ); return FAIL; } if ( strstr( from_file, "GRADE") ) { snprintf( action, BUFSIZ, "tried to submit grade file"); log_action( action ); return FAIL; } /* Find mode and current dir. */ if (!(mode = compute_mode( from_file ))){ snprintf(msg, BUFSIZ, "could not find the mode of %s", from_file); perror(msg); return FAIL; } if ( !getcwd( cwd, BUFSIZ ) ) { snprintf( msg, BUFSIZ, "getcwd failed"); perror(msg); return FAIL; } /* Compute complete source file and targ dir paths. */ if ( '/' == from_file[0]) { strcpy( from_path, from_file ); } else { snprintf(from_path, BUFSIZ, "%s/%s", cwd, from_file); } snprintf( targ_dir_path, BUFSIZ, "%s/%s", student_spool, targ_dir ); instructor_privi(); /* Check target. */ if ( FAIL == (isdir_return = isdir( targ_dir_path )) ) { fprintf( stderr, "isdir(%s) fails in submit_file", targ_dir_path ); return FAIL; } if ( ! isdir_return ) { fprintf( stderr, "%s is not a directory\n", targ_dir_path); return FAIL; } /* Compute target path and possibly remove old file. */ snprintf(targ_path, BUFSIZ, "%s/%s", targ_dir_path, endof_from_file ); if ( !stat( targ_path, &statfile) ) { fprintf( stderr, "File %s already exists.\n", targ_path ); if (FAIL == remove_file(targ_path) ) /* includes confirmation */ return FAIL; } /* Write the log entry before the action is performed. It seems to do more harm if there was action without a log entry than to find a log entry without an action. */ snprintf( action, BUFSIZ, "submitted %s", full_subm_path ); log_action( action ); /* If the source is a directory, just make a target dir. here */ user_privi(); if ( FAIL == (isdir_return = isdir( from_file )) ) { fprintf( stderr, "isdir(%s) fails in submit_file\n", from_file); return FAIL; } instructor_privi(); if ( isdir_return ) { mkdir( targ_path, mode ); } else { /* The actual copying, first into a file with new name. */ user_privi(); /* Segmentation fault may result if from_tmp is not used here */ snprintf(from_tmp, BUFSIZ, "/tmp/ShwXXXXXX"); temp_fd = mkstemp(from_tmp); if (FAIL == temp_fd){ snprintf(msg, BUFSIZ, "Could not create temporary name from_tmp."); perror(msg); return FAIL; } if (SUCC != copy_file_fd(from_file, temp_fd)){ snprintf(msg, BUFSIZ, "Could not copy into temporary file %s.", from_tmp); perror(msg); return FAIL; } local_root_privi(); if ( fchown( temp_fd, cstat.st_uid, cstat.st_gid ) ) { snprintf( msg, BUFSIZ, "chown(%s,,) failed", from_tmp ); perror( msg ); return FAIL; } instructor_privi(); if (FAIL == lseek(temp_fd, 0, SEEK_SET)){ snprintf(msg, BUFSIZ, "Could not lseek %s.", from_tmp); perror(msg); return FAIL; } if (SUCC != copy_fd_file(temp_fd, targ_path)){ snprintf(msg, BUFSIZ, "Could not copy %s into %s.", from_tmp, targ_path); perror(msg); return FAIL; } if (FAIL == close(temp_fd)){ snprintf(msg, BUFSIZ, "Could not close %s.", from_tmp); perror(msg); return FAIL; } /* Added in 2016 */ if (FAIL == unlink(from_tmp)){ snprintf(msg, BUFSIZ, "Could not remove %s.", from_tmp); perror(msg); return FAIL; } } /* Set ownership and permissions of the target. */ if ( chown( targ_path, cstat.st_uid, cstat.st_gid ) ) { snprintf( msg, BUFSIZ, "chown(%s,,) failed", targ_path ); perror( msg ); return FAIL; } if ( chmod( targ_path, mode ) ) { snprintf( msg, BUFSIZ, "chmod(%s, ) failed", targ_path ); perror( msg ); return FAIL; } /* If source was directory, go through it recursively. */ if ( isdir_return ) { user_privi(); if ( !(dirp = opendir( from_file )) ) { snprintf( msg, BUFSIZ, "Could not open directory %s", from_file ); perror( msg ); return FAIL; } while( (dp = readdir( dirp )) ) { if ( !strcmp( dp->d_name, ".") || !strcmp( dp->d_name, "..") ) continue; snprintf( from_file1, BUFSIZ, "%s/%s", from_file, dp->d_name ); snprintf( targ_dir1, BUFSIZ, "%s/%s", targ_dir, endof_from_file ); submit_file( from_file1, targ_dir1 ); } } return SUCC; } void submit_files(int argc, char *argv[]) { int i; char *from_file, newpath[BUFSIZ]; for ( i = 2; i < argc; i++ ) { if ( '-' == argv[i][0] ) { from_file = argv[i]+1; snprintf( newpath, BUFSIZ, "%s/%s", student_spool, from_file ); remove_file( newpath ); continue; } from_file = argv[i]; submit_file( from_file, "." ); } } int main( int argc, char **argv ) { char logname[BUFSIZ]; char *arg; int i; progname = endof_pathname(argv[0]); if ( 3 > argc ) usage(); pwd = *getpwuid( getuid() ); course = argv[1]; if (has_dot_dot( course )) return 1; snprintf( course_hw_dir, BUFSIZ, "%s/%s/%s", BASE, course, HOMEWORK ); instructor_privi(); if ( 0 > stat(course_hw_dir, &cstat) ) { snprintf( msg, BUFSIZ, "No homework directory for course %s.\n", course); perror(msg); return 1; } if ( CREATED == create_logfile_maybe( logname ) ) log_action( "action logging started" ); create_spool_maybe(); create_header_maybe(); /* Delete ending slash in all non-option (therefore filename) args. */ for (i = 2; i < argc; i++) { arg = argv[i]; if ( strcmp("-cat", arg) && strcmp("-cp", arg) && strcmp("-ls", arg) && strcmp("-rm", arg) ) delete_ending_slash(arg); } if ( !strcmp( argv[2], "-ls") ) { fprintf( stderr, "Files submitted to date for course %s:\n", course ); display_dir( student_spool, " ", stdout ); log_action( "queried submitted files" ); return 0; } if ( !strcmp( argv[2], "-cat" ) ) { for ( i = 3; i < argc; i++ ) show_file( argv[i] ); return 0; } if ( !strcmp( argv[2], "-cp" ) ) { if ( 3 == argc ) usage(); if ( 4 == argc ) { submit_file( argv[3], "." ); return 0; } for ( i = 3; i < argc - 1; i++ ) submit_file( argv[i], argv[argc - 1] ); return 0; } if ( !strcmp( argv[2], "-rm" ) || !strcmp ( argv[2], "--") ) { unsubmit_files( argc, argv ); return 0; } submit_files( argc, argv ); return 0; }