Index: checkin.c diff -c checkin.c:1.1.1.1 checkin.c:1.2 *** checkin.c:1.1.1.1 Tue Oct 6 23:40:23 1998 --- checkin.c Mon Dec 14 20:35:59 1998 *************** *** 78,84 **** call RCS_checkout here, compare the resulting files using xcmp, and rename if necessary. I think this should be fixed in RCS_cmp_file. */ ! if ((! preserve_perms && options != NULL && (strcmp (options, "-ko") == 0 || strcmp (options, "-kb") == 0)) --- 78,84 ---- call RCS_checkout here, compare the resulting files using xcmp, and rename if necessary. I think this should be fixed in RCS_cmp_file. */ ! if ((! preserve_perms && ! preserve_file_perms && options != NULL && (strcmp (options, "-ko") == 0 || strcmp (options, "-kb") == 0)) Index: client.c diff -c client.c:1.1.1.1 client.c:1.4 *** client.c:1.1.1.1 Tue Oct 6 23:40:23 1998 --- client.c Mon Dec 21 09:49:05 1998 *************** *** 144,149 **** --- 144,150 ---- static void handle_e PROTO((char *, int)); static void handle_f PROTO((char *, int)); static void handle_notified PROTO((char *, int)); + static void handle_preserve_permissions PROTO((char *, int)); static size_t try_read_from_server PROTO ((char *, size_t)); #endif /* CLIENT_SUPPORT */ *************** *** 164,188 **** mode_t mode; #endif /* __STDC__ */ { ! char buf[18], u[4], g[4], o[4]; int i; i = 0; if (mode & S_IRUSR) u[i++] = 'r'; if (mode & S_IWUSR) u[i++] = 'w'; if (mode & S_IXUSR) u[i++] = 'x'; u[i] = '\0'; i = 0; if (mode & S_IRGRP) g[i++] = 'r'; if (mode & S_IWGRP) g[i++] = 'w'; if (mode & S_IXGRP) g[i++] = 'x'; g[i] = '\0'; i = 0; if (mode & S_IROTH) o[i++] = 'r'; if (mode & S_IWOTH) o[i++] = 'w'; if (mode & S_IXOTH) o[i++] = 'x'; o[i] = '\0'; sprintf(buf, "u=%s,g=%s,o=%s", u, g, o); --- 165,192 ---- mode_t mode; #endif /* __STDC__ */ { ! char buf[21], u[5], g[5], o[5]; int i; i = 0; if (mode & S_IRUSR) u[i++] = 'r'; if (mode & S_IWUSR) u[i++] = 'w'; if (mode & S_IXUSR) u[i++] = 'x'; + if (mode & S_ISUID) u[i++] = 's'; u[i] = '\0'; i = 0; if (mode & S_IRGRP) g[i++] = 'r'; if (mode & S_IWGRP) g[i++] = 'w'; if (mode & S_IXGRP) g[i++] = 'x'; + if (mode & S_ISGID) g[i++] = 's'; g[i] = '\0'; i = 0; if (mode & S_IROTH) o[i++] = 'r'; if (mode & S_IWOTH) o[i++] = 'w'; if (mode & S_IXOTH) o[i++] = 'x'; + if (mode & S_ISVTX) o[i++] = 's'; o[i] = '\0'; sprintf(buf, "u=%s,g=%s,o=%s", u, g, o); *************** *** 247,253 **** { if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=') { ! int can_read = 0, can_write = 0, can_execute = 0; char *q = p + 2; while (*q != ',' && *q != '\0') { --- 251,257 ---- { if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=') { ! int can_read = 0, can_write = 0, can_execute = 0, is_sticky = 0; char *q = p + 2; while (*q != ',' && *q != '\0') { *************** *** 257,262 **** --- 261,268 ---- can_write = 1; else if (*q == 'x') can_execute = 1; + else if (*q == 's') + is_sticky = 1; ++q; } if (p[0] == 'u') *************** *** 267,272 **** --- 273,280 ---- mode |= S_IWUSR; if (can_execute) mode |= S_IXUSR; + if (is_sticky) + mode |= S_ISUID; } else if (p[0] == 'g') { *************** *** 276,281 **** --- 284,291 ---- mode |= S_IWGRP; if (can_execute) mode |= S_IXGRP; + if (is_sticky) + mode |= S_ISGID; } else if (p[0] == 'o') { *************** *** 285,290 **** --- 295,302 ---- mode |= S_IWOTH; if (can_execute) mode |= S_IXOTH; + if (is_sticky) + mode |= S_ISVTX; } } /* Skip to the next field. */ *************** *** 294,300 **** ++p; } ! if (respect_umask) { oumask = umask (0); (void) umask (oumask); --- 306,312 ---- ++p; } ! if ((! preserve_file_perms) && respect_umask) { oumask = umask (0); (void) umask (oumask); *************** *** 1958,1965 **** free (buf); } ! if (stored_mode_valid) change_mode (filename, stored_mode, 1); stored_mode_valid = 0; if (stored_modtime_valid) --- 1970,1978 ---- free (buf); } ! if (stored_mode_valid) { change_mode (filename, stored_mode, 1); + } stored_mode_valid = 0; if (stored_modtime_valid) *************** *** 2944,2950 **** --- 2957,2982 ---- } + /* Get the PreservePermissions setting from the server -- assume + this is the exact string as found in the config file (although + it may not be at the moment) */ + static void + handle_preserve_permissions (args, len) + char *args; + int len; + { + /* if (strcmp(args, "yes") == 0) + preserve_perms = 1; /* Don't handle 'yes' for now -- client support has not been developed and it will most likely cause problems */ + + if (strcmp(args, "file_perms") == 0) + preserve_file_perms = 1; + + return; + } + + + static void handle_m (args, len) char *args; int len; *************** *** 3140,3145 **** --- 3172,3180 ---- RSP_LINE("E", handle_e, response_type_normal, rs_essential), RSP_LINE("F", handle_f, response_type_normal, rs_optional), RSP_LINE("MT", handle_mt, response_type_normal, rs_optional), + RSP_LINE("PreservePermissions", handle_preserve_permissions, + response_type_normal, + rs_optional), /* Possibly should be response_type_error. */ RSP_LINE(NULL, NULL, response_type_normal, rs_essential) *************** *** 4222,4227 **** --- 4257,4273 ---- error (1, 0, "This server does not support the global -l option."); } + } + + /* Get PreservePermissions from the server. */ + + if (supported_request ("sendme-PreservePermissions")) + { + int err; + send_to_server ("sendme-PreservePermissions\012", 0); + err = get_server_responses (); + if (err != 0) + error (err, 0, "error reading from server"); } /* Find out about server-side cvswrappers. An extra network Index: cvs.h diff -c cvs.h:1.1.1.1 cvs.h:1.2 *** cvs.h:1.1.1.1 Tue Oct 6 23:40:22 1998 --- cvs.h Mon Dec 14 20:36:00 1998 *************** *** 736,741 **** --- 736,743 ---- /* TODO: can the finfo argument to special_file_mismatch be changed? -twp */ int special_file_mismatch PROTO ((struct file_info *finfo, char *rev1, char *rev2)); + int special_file_mismatch_file_perms_only PROTO ((struct file_info *finfo, + char *rev1, char *rev2)); /* CVSADM_BASEREV stuff, from entries.c. */ extern char *base_get PROTO ((struct file_info *)); Index: filesubr.c diff -c filesubr.c:1.1.1.1 filesubr.c:1.2 *** filesubr.c:1.1.1.1 Tue Oct 6 23:40:30 1998 --- filesubr.c Mon Dec 14 20:36:00 1998 *************** *** 353,359 **** struct stat sb; mode_t mode, oumask; ! if (preserve_perms) return; if (stat (fname, &sb) < 0) --- 353,359 ---- struct stat sb; mode_t mode, oumask; ! if (preserve_perms || preserve_file_perms) return; if (stat (fname, &sb) < 0) Index: import.c diff -c import.c:1.1.1.1 import.c:1.2 *** import.c:1.1.1.1 Tue Oct 6 23:40:25 1998 --- import.c Mon Dec 14 20:36:01 1998 *************** *** 1158,1163 **** --- 1158,1168 ---- } } } + else if (preserve_file_perms) { + if (fprintf (fprcs, "permissions\t%o;\012", + sb.st_mode & 07777) < 0) + goto write_error; + } #endif if (add_vbranch != NULL) *************** *** 1208,1213 **** --- 1213,1223 ---- userfile); } } + } + else if (preserve_file_perms) { + if (fprintf (fprcs, "permissions\t%o;\012", + sb.st_mode & 07777) < 0) + goto write_error; } #endif Index: mkmodules.c diff -c mkmodules.c:1.1.1.1 mkmodules.c:1.2 *** mkmodules.c:1.1.1.1 Tue Oct 6 23:40:26 1998 --- mkmodules.c Mon Dec 21 10:54:29 1998 *************** *** 281,287 **** "#SystemAuth=no\n", "\n", "# Set `PreservePermissions' to `yes' to save file status information\n", ! "# in the repository.\n", "#PreservePermissions=no\n", "\n", "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n", --- 281,288 ---- "#SystemAuth=no\n", "\n", "# Set `PreservePermissions' to `yes' to save file status information\n", ! "# in the repository. Set it to 'file_perms' to just save file\n", ! "# permissions\n", "#PreservePermissions=no\n", "\n", "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n", Index: no_diff.c diff -c no_diff.c:1.1.1.1 no_diff.c:1.2 *** no_diff.c:1.1.1.1 Tue Oct 6 23:40:26 1998 --- no_diff.c Mon Dec 14 20:36:01 1998 *************** *** 41,46 **** --- 41,48 ---- information also means that the files should be considered different. */ if (preserve_perms && special_file_mismatch (finfo, vers->vn_user, NULL)) return 1; + if (preserve_file_perms && special_file_mismatch_file_perms_only (finfo, vers->vn_user, NULL)) + return 1; #endif if (vers->entdata && vers->entdata->options) Index: options.h.in diff -c options.h.in:1.1.1.1 options.h.in:1.2 *** options.h.in:1.1.1.1 Tue Oct 6 23:40:22 1998 --- options.h.in Mon Dec 14 20:36:01 1998 *************** *** 67,72 **** --- 67,81 ---- #endif /* + * The default file mode for files that are in the repository but don't have + * a 'permissions' node -- usually when preserve_file_perms is turned on + * on a pre-existing repository. + */ + #ifndef FILE_MODE_DFLT + #define FILE_MODE_DFLT 0600 + #endif + + /* * The cvs admin command is restricted to the members of the group * CVS_ADMIN_GROUP. If this group does not exist, all users are * allowed to run cvs admin. To disable the cvs admin for all users, Index: parseinfo.c diff -c parseinfo.c:1.1.1.1 parseinfo.c:1.2 *** parseinfo.c:1.1.1.1 Tue Oct 6 23:40:27 1998 --- parseinfo.c Mon Dec 14 20:36:01 1998 *************** *** 339,344 **** --- 339,347 ---- warning: this CVS does not support PreservePermissions"); #endif } + else if (strcmp(p, "file_perms") == 0) { + preserve_file_perms = 1; + } else { error (0, 0, "unrecognized value '%s' for PreservePermissions", Index: rcs.c diff -c rcs.c:1.1.1.1 rcs.c:1.3 *** rcs.c:1.1.1.1 Tue Oct 6 23:40:27 1998 --- rcs.c Wed Dec 16 13:34:56 1998 *************** *** 14,19 **** --- 14,20 ---- #include "hardlink.h" int preserve_perms = 0; + int preserve_file_perms = 0; /* The RCS -k options, and a set of enums that must match the array. These come first so that we can use enum kflag in function *************** *** 4443,4448 **** --- 4444,4467 ---- workfile, info->data); } } + else if (preserve_file_perms) + { + RCSVers *vers; + Node *info; + + vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); + if (vp == NULL) + error (1, 0, "internal error: no revision information for %s", + rev == NULL ? rcs->head : rev); + vers = (RCSVers *) vp->data; + + info = findnode (vers->other_delta, "permissions"); + if (info != NULL) + { + change_rcs_mode = 1; + rcs_mode = (mode_t) strtoul (info->data, NULL, 8); + } + } #endif if (expand != KFLAG_O && expand != KFLAG_B) *************** *** 4753,4758 **** --- 4772,4800 ---- return (RCSVers *) p->data; } + + mode_t + RCS_get_perms (rcs, rev) + RCSNode *rcs; + char *rev; + { + RCSVers *vers; + Node *info; + Node *vp = NULL; + + vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); + if (vp == NULL) + error (1, 0, "internal error: no revision information for %s", + rev == NULL ? rcs->head : rev); + vers = (RCSVers *) vp->data; + + info = findnode (vers->other_delta, "permissions"); + if (info != NULL) + return (mode_t) strtoul (info->data, NULL, 8); + + return (mode_t) 0; + } + /* Revision number string, R, must contain a `.'. Return a newly-malloc'd copy of the prefix of R up to but not including the final `.'. */ *************** *** 5154,5159 **** --- 5196,5218 ---- /* Save hardlinks. */ delta->hardlinks = list_linked_files_on_disk (workfile); } + } + else if (preserve_file_perms) + { + Node *np; + struct stat sb; + char buf[64]; /* static buffer should be safe: see usage. -twp */ + + delta->other_delta = getlist(); + + if (CVS_LSTAT (workfile, &sb) < 0) + error (1, 1, "cannot lstat %s", workfile); + + (void) sprintf (buf, "%o", sb.st_mode & 07777); + np = getnode(); + np->key = xstrdup ("permissions"); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); } #endif Index: rcs.h diff -c rcs.h:1.1.1.1 rcs.h:1.2 *** rcs.h:1.1.1.1 Tue Oct 6 23:40:22 1998 --- rcs.h Mon Dec 14 20:36:03 1998 *************** *** 231,236 **** --- 231,237 ---- char *make_file_label PROTO ((char *, char *, RCSNode *)); extern int preserve_perms; + extern int preserve_file_perms; /* From import.c. */ extern int add_rcs_file PROTO ((char *, char *, char *, char *, char *, Index: server.c diff -c server.c:1.1.1.1 server.c:1.4 *** server.c:1.1.1.1 Tue Oct 6 23:40:28 1998 --- server.c Wed Dec 16 13:34:57 1998 *************** *** 3892,3898 **** --- 3892,3918 ---- } + /* Tell the client about PreservePermissions options set in CVSROOT/config. + Currently, rather than reading the exact string from the config file, + we regenerate the string based on the variables we know are PreservePermission's + related. Ideally we would save the PreservePermissions string when we read + it the first time, then pass that as is to the client. */ static void + serve_preserve_permissions (arg) + char *arg; + { + buf_output0 (buf_to_net, "PreservePermissions "); + buf_output0 (buf_to_net, (preserve_perms ? "yes" : (preserve_file_perms ? "file_perms" : "no"))); + buf_output0 (buf_to_net, "\012"); + + buf_output0 (buf_to_net, "ok\012"); + + /* The client is waiting for us, so we better send the data now. */ + buf_flush (buf_to_net, 1); + } + + + static void serve_ignore (arg) char *arg; { *************** *** 4204,4209 **** --- 4224,4230 ---- REQ_LINE("init", serve_init, rq_optional), REQ_LINE("annotate", serve_annotate, rq_optional), REQ_LINE("noop", serve_noop, rq_optional), + REQ_LINE("sendme-PreservePermissions", serve_preserve_permissions, rq_optional), REQ_LINE(NULL, NULL, rq_optional) #undef REQ_LINE Index: update.c diff -c update.c:1.1.1.1 update.c:1.4 *** update.c:1.1.1.1 Tue Oct 6 23:40:30 1998 --- update.c Mon Dec 21 19:13:30 1998 *************** *** 1293,1299 **** if (stat (vers_ts->srcfile->path, &sb) < 0) error (1, errno, "cannot stat %s", vers_ts->srcfile->path); ! mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH); } if (cvswrite --- 1293,1302 ---- if (stat (vers_ts->srcfile->path, &sb) < 0) error (1, errno, "cannot stat %s", vers_ts->srcfile->path); ! if (preserve_file_perms) ! mode = RCS_get_perms(vers_ts->srcfile, vers_ts->vn_rcs); ! else ! mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH); } if (cvswrite *************** *** 1302,1308 **** { if (revbuf == NULL) xchmod (finfo->file, 1); ! else { /* We know that we are the server here, so although xchmod checks umask, we don't bother. */ --- 1305,1311 ---- { if (revbuf == NULL) xchmod (finfo->file, 1); ! else if (! preserve_file_perms) /* don't mess with the mode if preserve_file_perms is set */ { /* We know that we are the server here, so although xchmod checks umask, we don't bother. */ *************** *** 1686,1692 **** if (CVS_STAT (vers_ts->srcfile->path, file_info) < 0) error (1, errno, "could not stat %s", vers_ts->srcfile->path); if (chmod (finfo->file, ! file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)) < 0) error (0, errno, "cannot change mode of file %s", finfo->file); if (cvswrite --- 1689,1699 ---- if (CVS_STAT (vers_ts->srcfile->path, file_info) < 0) error (1, errno, "could not stat %s", vers_ts->srcfile->path); if (chmod (finfo->file, ! (preserve_file_perms ? ! RCS_get_perms(vers_ts->srcfile, vers_ts->vn_rcs) ! : ! file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) ! )) < 0) error (0, errno, "cannot change mode of file %s", finfo->file); if (cvswrite *************** *** 2766,2771 **** --- 2773,2928 ---- dellist (&rev1_hardlinks); if (rev2_hardlinks != NULL) dellist (&rev2_hardlinks); + + return result; + #else + return 0; + #endif + } + + int + special_file_mismatch_file_perms_only (finfo, rev1, rev2) + struct file_info *finfo; + char *rev1; + char *rev2; + { + #ifdef PRESERVE_PERMISSIONS_SUPPORT + struct stat sb; + RCSVers *vp; + Node *n; + uid_t rev1_uid, rev2_uid; + gid_t rev1_gid, rev2_gid; + mode_t rev1_mode, rev2_mode; + unsigned long dev_long; + dev_t rev1_dev, rev2_dev; + char *rev1_symlink = NULL; + char *rev2_symlink = NULL; + List *rev1_hardlinks; + List *rev2_hardlinks; + int check_uids, check_gids, check_modes; + int result; + + /* If we don't care about special file info, then + don't report a mismatch in any case. */ + if (!preserve_file_perms) + return 0; + + /* When special_file_mismatch is called from No_Difference, the + RCS file has been only partially parsed. We must read the + delta tree in order to compare special file info recorded in + the delta nodes. (I think this is safe. -twp) */ + if (finfo->rcs->flags & PARTIAL) + RCS_reparsercsfile (finfo->rcs, NULL, NULL); + + check_modes = 1; + + /* Obtain file information for REV1. If this is null, then stat + finfo->file and use that info. */ + /* If a revision does not know anything about its status, + then presumably it doesn't matter, and indicates no conflict. */ + + if (rev1 == NULL) + { + if (islink (finfo->file)) + rev1_symlink = xreadlink (finfo->file); + else + { + if (CVS_LSTAT (finfo->file, &sb) < 0) + error (1, errno, "could not get file information for %s", + finfo->file); + rev1_mode = sb.st_mode; + } + } + else + { + n = findnode (finfo->rcs->versions, rev1); + vp = (RCSVers *) n->data; + + n = findnode (vp->other_delta, "symlink"); + if (n != NULL) + rev1_symlink = xstrdup (n->data); + else + { + n = findnode (vp->other_delta, "permissions"); + if (n == NULL) + rev1_mode = FILE_MODE_DFLT; + else + rev1_mode = strtoul (n->data, NULL, 8); + } + } + + /* Obtain file information for REV2. */ + if (rev2 == NULL) + { + if (islink (finfo->file)) + rev2_symlink = xreadlink (finfo->file); + else + { + if (CVS_LSTAT (finfo->file, &sb) < 0) + error (1, errno, "could not get file information for %s", + finfo->file); + rev2_mode = sb.st_mode; + } + } + else + { + n = findnode (finfo->rcs->versions, rev2); + vp = (RCSVers *) n->data; + + n = findnode (vp->other_delta, "symlink"); + if (n != NULL) + rev2_symlink = xstrdup (n->data); + else + { + n = findnode (vp->other_delta, "permissions"); + if (n == NULL) + check_modes = 0; /* don't care */ + else + rev2_mode = strtoul (n->data, NULL, 8); + } + } + + /* Check the user/group ownerships and file permissions, printing + an error for each mismatch found. Return 0 if all characteristics + matched, and 1 otherwise. */ + + result = 0; + + /* Compare symlinks first, since symlinks are simpler (don't have + any other characteristics). */ + /* if (rev1_symlink != NULL && rev2_symlink == NULL) + { + error (0, 0, "%s is a symbolic link", + (rev1 == NULL ? "working file" : rev1)); + result = 1; + } + else if (rev1_symlink == NULL && rev2_symlink != NULL) + { + error (0, 0, "%s is a symbolic link", + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + else if (rev1_symlink != NULL) + */ if (rev1_symlink != NULL) + result = (strcmp (rev1_symlink, rev2_symlink) == 0); + else + { + /* Compare permissions. */ + if (check_modes && + (rev1_mode & 07777) != (rev2_mode & 07777)) + { + error (0, 0, "%s: permission mismatch between %s and %s", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + } + + if (rev1_symlink != NULL) + free (rev1_symlink); + if (rev2_symlink != NULL) + free (rev2_symlink); return result; #else