diff --git a/ChangeLog b/ChangeLog index 8f36ee6ae..09b7ce622 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Sat Feb 20 20:30:00 1999 Richard Frith-Macdonald + + * Source/NSUser.m: Added GSSetUserName() and rewrote NSUserName() to + work consistently - use the LOGNAME environment variable as first + choice of username. + * Source/include/NSPathUtilities.h: Added GSSetUserName() + * Source/NSUserDefaults.m: Added GNUstep specific method - + ([+resetUserDefaults]) to reset the standard defaults in the event of + a change os username. Also changed to create defaults withe correct + ownership, and to create a defaults file containing a valid (empty) + dictionary. + * Source/include/NSUserDefaults.h: Added ([+resetUserDefaults]) + * Source/NSData.m: Try to preserve file ownership etc with + ([-writeToFile:atomically:]) + * Source/NSFileManager.m: ([-changeFileAttributes:atPath:]) added + code to handle owner and group names if owner and group IDs are not + supplied. Also added code to ensure that programs running setuid to + root create new files and directories with the ownership of their + actual login user by default. + 1999-02-19 Michael Hanni #include -extern NSString *NSUserName (); -extern NSString *NSHomeDirectory (); -extern NSString *NSHomeDirectoryForUser (NSString *userName); +#ifndef NO_GNUSTEP +/* + * This extension permits a change of username from that specified in the + * LOGNAME environment variable. Using it will almost certainly cause + * trouble if the process does not posess the file access priviliges of the + * new name. This is provided primarily for use by processes that run as + * system-manager and need to act as particular users. If uses the + * [NSUserDefaults +resetUserDefaults] extension to reset the defaults system + * to use the defaults belonging to the new user. + */ +extern void GSSetUserName(NSString *name); +#endif +extern NSString *NSUserName(); +extern NSString *NSHomeDirectory(); +extern NSString *NSHomeDirectoryForUser(NSString *userName); #ifndef STRICT_OPENSTEP extern NSString *NSFullUserName(void); diff --git a/Headers/gnustep/base/NSUserDefaults.h b/Headers/gnustep/base/NSUserDefaults.h index 0883bd933..a1660ee33 100644 --- a/Headers/gnustep/base/NSUserDefaults.h +++ b/Headers/gnustep/base/NSUserDefaults.h @@ -124,6 +124,13 @@ extern NSString* const NSDateTimeOrdering; /* Getting the Shared Instance */ + (NSUserDefaults*) standardUserDefaults; +#ifndef NO_GNUSTEP +/* + * Called by GSSetUserName() to get the defaults system to use the defaults + * of a new user. + */ ++ (void) resetUserDefaults; +#endif + (NSArray*) userLanguages; + (void) setUserLanguages: (NSArray*)languages; diff --git a/Source/NSData.m b/Source/NSData.m index 649387c07..4bc34e844 100644 --- a/Source/NSData.m +++ b/Source/NSData.m @@ -71,6 +71,8 @@ #include #include #include +#include +#include #include /* for memset() */ #include /* SEEK_* on SunOS 4 */ @@ -500,6 +502,7 @@ failure: NSLog(@"Open (%s) attempt failed - bad path", theRealPath); return NO; } + #ifdef HAVE_MKSTEMP if (useAuxiliaryFile) { @@ -576,17 +579,43 @@ failure: } /* If we used a temporary file, we still need to rename() it be the - * real file. Am I forgetting anything here? */ + * real file. Also, we need to try to retain the file attributes of + * the original file we are overwriting (if we are) */ if (useAuxiliaryFile) { - c = rename(thePath, theRealPath); + NSFileManager *mgr = [NSFileManager defaultManager]; + NSMutableDictionary *att = nil; - if (c != 0) /* Many things could go wrong, I - * guess. */ + if ([mgr fileExistsAtPath: path]) + { + att = [[mgr fileAttributesAtPath:path traverseLink:YES] mutableCopy]; + if (att) + [att autorelease]; + } + + c = rename(thePath, theRealPath); + if (c != 0) /* Many things could go wrong, I guess. */ { NSLog(@"Rename (%s) failed - %s", thePath, strerror(errno)); goto failure; } + + if (att) + { + /* + * We have created a new file - so we attempt to make it's + * attributes match that of the original. + */ + [att removeObjectForKey: NSFileSize]; + [att removeObjectForKey: NSFileModificationDate]; + [att removeObjectForKey: NSFileReferenceCount]; + [att removeObjectForKey: NSFileSystemNumber]; + [att removeObjectForKey: NSFileSystemFileNumber]; + [att removeObjectForKey: NSFileDeviceIdentifier]; + [att removeObjectForKey: NSFileType]; + if ([mgr changeFileAttributes: att atPath: path] == NO) + NSLog(@"Unable to correctly set all attributes for '%@'", path); + } } /* success: */ diff --git a/Source/NSFileManager.m b/Source/NSFileManager.m index f3bd06b01..6a3160a1b 100644 --- a/Source/NSFileManager.m +++ b/Source/NSFileManager.m @@ -184,63 +184,93 @@ static NSFileManager* defaultManager = nil; #endif } -- (BOOL)createDirectoryAtPath:(NSString*)path - attributes:(NSDictionary*)attributes +- (BOOL) createDirectoryAtPath:(NSString*)path + attributes:(NSDictionary*)attributes { #if defined(__WIN32__) || defined(_WIN32) return CreateDirectory([self fileSystemRepresentationWithPath: path], NULL); #else - const char* cpath; - char dirpath[PATH_MAX+1]; - struct stat statbuf; - int len, cur; + const char* cpath; + char dirpath[PATH_MAX+1]; + struct stat statbuf; + int len, cur; + NSDictionary *needChown = nil; - cpath = [self fileSystemRepresentationWithPath:path]; - len = strlen(cpath); - if (len > PATH_MAX) - // name too long - return NO; - - if (strcmp(cpath, "/") == 0 || len == 0) - // cannot use "/" or "" as a new dir path - return NO; - - strcpy(dirpath, cpath); - dirpath[len] = '\0'; - if (dirpath[len-1] == '/') - dirpath[len-1] = '\0'; - cur = 0; - - do { - // find next '/' - while (dirpath[cur] != '/' && cur < len) - cur++; - // if first char is '/' then again; (cur == len) -> last component - if (cur == 0) { - cur++; - continue; - } - // check if path from 0 to cur is valid - dirpath[cur] = '\0'; - if (stat(dirpath, &statbuf) == 0) { - if (cur == len) - return NO; // already existing last path - } - else { - // make new directory - if (mkdir(dirpath, 0777) != 0) - return NO; // could not create component - // if last directory and attributes then change - if (cur == len && attributes) - return [self changeFileAttributes:attributes - atPath:[self stringWithFileSystemRepresentation:dirpath - length:cur]]; - } - dirpath[cur] = '/'; - cur++; - } while (cur < len); + /* + * If there is no file owner specified, and we are running setuid to + * root, then we assume we need to change ownership to correct user. + */ + if ([attributes objectForKey: NSFileOwnerAccountName] == nil + && [attributes objectForKey: NSFileOwnerAccountNumber] == nil + && geteuid() == 0 && [@"root" isEqualToString: NSUserName()] == NO) + { + needChown = [NSDictionary dictionaryWithObjectsAndKeys: + NSFileOwnerAccountName, NSUserName(), nil]; + } - return YES; + cpath = [self fileSystemRepresentationWithPath:path]; + len = strlen(cpath); + if (len > PATH_MAX) // name too long + return NO; + + if (strcmp(cpath, "/") == 0 || len == 0) // cannot use "/" or "" + return NO; + + strcpy(dirpath, cpath); + dirpath[len] = '\0'; + if (dirpath[len-1] == '/') + dirpath[len-1] = '\0'; + cur = 0; + + do + { + // find next '/' + while (dirpath[cur] != '/' && cur < len) + cur++; + // if first char is '/' then again; (cur == len) -> last component + if (cur == 0) + { + cur++; + continue; + } + // check if path from 0 to cur is valid + dirpath[cur] = '\0'; + if (stat(dirpath, &statbuf) == 0) + { + if (cur == len) + return NO; // already existing last path + } + else + { + // make new directory + if (mkdir(dirpath, 0777) != 0) + return NO; // could not create component + // if last directory and attributes then change + if (cur == len && attributes) + { + if ([self changeFileAttributes:attributes + atPath:[self stringWithFileSystemRepresentation:dirpath + length:cur]] == NO) + return NO; + if (needChown) + { + if ([self changeFileAttributes: needChown + atPath:[self stringWithFileSystemRepresentation:dirpath + length:cur]] == NO) + { + NSLog(@"Failed to change ownership of '%s' to '%@'", + dirpath, NSUserName()); + } + } + return YES; + } + } + dirpath[cur] = '/'; + cur++; + } + while (cur < len); + + return YES; #endif /* WIN32 */ } @@ -267,7 +297,7 @@ static NSFileManager* defaultManager = nil; // File operations - (BOOL)copyPath:(NSString*)source toPath:(NSString*)destination - handler:handler + handler:handler { BOOL sourceIsDir, fileExists; NSDictionary* attributes; @@ -327,7 +357,7 @@ static NSFileManager* defaultManager = nil; } - (BOOL)movePath:(NSString*)source toPath:(NSString*)destination - handler:handler + handler:handler { BOOL sourceIsDir, fileExists; const char* sourcePath = [self fileSystemRepresentationWithPath:source]; @@ -485,29 +515,48 @@ static NSFileManager* defaultManager = nil; } } -- (BOOL)createFileAtPath:(NSString*)path contents:(NSData*)contents - attributes:(NSDictionary*)attributes +- (BOOL) createFileAtPath: (NSString*)path + contents: (NSData*)contents + attributes: (NSDictionary*)attributes { - int fd, len, written; + int fd, len, written; - fd = open ([self fileSystemRepresentationWithPath:path], + fd = open ([self fileSystemRepresentationWithPath:path], O_WRONLY|O_TRUNC|O_CREAT, 0644); - if (fd < 0) - return NO; + if (fd < 0) + return NO; - if (![self changeFileAttributes:attributes atPath:path]) { - close (fd); - return NO; + if (![self changeFileAttributes: attributes atPath: path]) + { + close (fd); + return NO; } - len = [contents length]; - if (len) - written = write (fd, [contents bytes], len); - else - written = 0; - close (fd); + /* + * If there is no file owner specified, and we are running setuid to + * root, then we assume we need to change ownership to correct user. + */ + if ([attributes objectForKey: NSFileOwnerAccountName] == nil + && [attributes objectForKey: NSFileOwnerAccountNumber] == nil + && geteuid() == 0 && [@"root" isEqualToString: NSUserName()] == NO) + { + attributes = [NSDictionary dictionaryWithObjectsAndKeys: + NSFileOwnerAccountName, NSUserName(), nil]; + if (![self changeFileAttributes: attributes atPath: path]) + { + NSLog(@"Failed to change ownership of '%@' to '%@'", + path, NSUserName()); + } + } - return written == len; + len = [contents length]; + if (len) + written = write (fd, [contents bytes], len); + else + written = 0; + close (fd); + + return written == len; } // Getting and comparing file contents @@ -781,55 +830,95 @@ static NSFileManager* defaultManager = nil; #endif /* WIN32 */ } -- (BOOL)changeFileAttributes:(NSDictionary*)attributes atPath:(NSString*)path +- (BOOL) changeFileAttributes: (NSDictionary*)attributes atPath: (NSString*)path { - const char* cpath = [self fileSystemRepresentationWithPath:path]; - NSNumber* num; - NSDate* date; - BOOL allOk = YES; + const char *cpath = [self fileSystemRepresentationWithPath:path]; + NSNumber *num; + NSString *str; + NSDate *date; + BOOL allOk = YES; #ifndef __WIN32__ - num = [attributes objectForKey:NSFileOwnerAccountNumber]; - if (num) { - allOk &= (chown(cpath, [num intValue], -1) == 0); + num = [attributes objectForKey: NSFileOwnerAccountNumber]; + if (num) + { + allOk &= (chown(cpath, [num intValue], -1) == 0); } - - num = [attributes objectForKey:NSFileGroupOwnerAccountNumber]; - if (num) { - allOk &= (chown(cpath, -1, [num intValue]) == 0); - } -#endif - - num = [attributes objectForKey:NSFilePosixPermissions]; - if (num) { - allOk &= (chmod(cpath, [num intValue]) == 0); - } - - date = [attributes objectForKey:NSFileModificationDate]; - if (date) { - struct stat sb; -#ifdef _POSIX_VERSION - struct utimbuf ub; + else + { + if ((str = [attributes objectForKey:NSFileOwnerAccountName]) != nil) + { +#if HAVE_PWD_H + struct passwd *pw = getpwnam([str cString]); + + if (pw) + { + allOk &= (chown(cpath, pw->pw_uid, -1) == 0); + chown(cpath, -1, pw->pw_gid); + } + else + allOk = NO; #else - time_t ub[2]; + allOk = NO; +#endif + } + } + + num = [attributes objectForKey:NSFileGroupOwnerAccountNumber]; + if (num) + { + allOk &= (chown(cpath, -1, [num intValue]) == 0); + } + else if ((str = [attributes objectForKey:NSFileGroupOwnerAccountName]) != nil) +#if HAVE_GRP_H + { + struct group *gp = getgrnam([str cString]); + + if (gp) + { + allOk &= (chown(cpath, -1, gp->gr_gid) == 0); + } + else + allOk = NO; + } +#else + allOk = NO; +#endif #endif - if (stat(cpath, &sb) != 0) - allOk = NO; - else { + num = [attributes objectForKey:NSFilePosixPermissions]; + if (num) + { + allOk &= (chmod(cpath, [num intValue]) == 0); + } + + date = [attributes objectForKey:NSFileModificationDate]; + if (date) + { + struct stat sb; #ifdef _POSIX_VERSION - ub.actime = sb.st_atime; - ub.modtime = [date timeIntervalSince1970]; - allOk &= (utime(cpath, &ub) == 0); + struct utimbuf ub; #else - ub[0] = sb.st_atime; - ub[1] = [date timeIntervalSince1970]; - allOk &= (utime((char*)cpath, ub) == 0); + time_t ub[2]; +#endif + + if (stat(cpath, &sb) != 0) + allOk = NO; + else + { +#ifdef _POSIX_VERSION + ub.actime = sb.st_atime; + ub.modtime = [date timeIntervalSince1970]; + allOk &= (utime(cpath, &ub) == 0); +#else + ub[0] = sb.st_atime; + ub[1] = [date timeIntervalSince1970]; + allOk &= (utime((char*)cpath, ub) == 0); #endif } } - return allOk; + return allOk; } // Discovering directory contents diff --git a/Source/NSUser.m b/Source/NSUser.m index 38222f036..e6e6b00d7 100644 --- a/Source/NSUser.m +++ b/Source/NSUser.m @@ -26,11 +26,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include // for getenv() #if !defined(__WIN32__) && !defined(_WIN32) @@ -39,38 +41,62 @@ #endif #include -/* Return the caller's login name as an NSString object. */ +static NSString *theUserName = nil; + +void +GSSetUserName(NSString* name) +{ + if (theUserName == nil) + theUserName = RETAIN(name); + else if ([theUserName isEqualToString: name] == NO) + { + ASSIGN(theUserName, name); + [NSUserDefaults resetUserDefaults]; + } +} + +/* + * Return the caller's login name as an NSString object. + * The 'LOGNAME' environment variable is our primary source, but we use + * other system-dependent sources if LOGNAME is not set. + */ NSString * NSUserName () { + if (theUserName == nil) + { + const char *login_name = 0; #if defined(__WIN32__) || defined(_WIN32) - /* The GetUserName function returns the current user name */ - char buf[1024]; - DWORD n = 1024; + /* The GetUserName function returns the current user name */ + char buf[1024]; + DWORD n = 1024; - if (GetUserName(buf, &n)) - return [NSString stringWithCString: buf]; - else - return [NSString stringWithCString: ""]; -#elif __SOLARIS__ || defined(BSD) - int uid = geteuid(); // get the effective user id - struct passwd *pwent = getpwuid (uid); - NSString* name = [NSString stringWithCString: pwent->pw_name]; - return name; + if (GetEnvironmentVariable("LOGNAME", buf, 1024); + login_name = buf; + else if (GetUserName(buf, &n)) + login_name = buf; #else - const char *login_name = getlogin (); - - if (!login_name) - login_name = cuserid(NULL); - - if (!login_name) - login_name= getenv ("LOGNAME"); - - if (login_name) - return [NSString stringWithCString: login_name]; - else - return nil; + login_name = getenv("LOGNAME"); + if (login_name == 0 || getpwnam(login_name) == 0) + { +# if __SOLARIS__ || defined(BSD) + int uid = geteuid(); // get the effective user id + struct passwd *pwent = getpwuid (uid); + login_name = pwent->pw_name; +# else + login_name = getlogin(); + if (!login_name) + login_name = cuserid(NULL); +# endif + } #endif + if (login_name) + GSSetUserName([NSString stringWithCString: login_name]); + else + [NSException raise: NSInternalInconsistencyException + format: @"Unable to determine curren user name"]; + } + return theUserName; } /* Return the caller's home directory as an NSString object. */ @@ -78,8 +104,6 @@ NSString * NSHomeDirectory () { return NSHomeDirectoryForUser (NSUserName ()); - /* xxx Was using this. Is there a reason to prefer it? - return [NSString stringWithCString: getenv ("HOME")]; */ } /* Return LOGIN_NAME's home directory as an NSString object. */ diff --git a/Source/NSUserDefaults.m b/Source/NSUserDefaults.m index 6109290a5..5c868130e 100644 --- a/Source/NSUserDefaults.m +++ b/Source/NSUserDefaults.m @@ -79,6 +79,17 @@ static NSMutableString *processName = nil; /************************************************************************* *** Getting the Shared Instance *************************************************************************/ +static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */ + ++ (void) resetUserDefaults +{ + id defs = sharedDefaults; + + setSharedDefaults = NO; + sharedDefaults = nil; + [defs release]; +} + + (NSUserDefaults *)standardUserDefaults /* Returns the shared defaults object. If it doesn't exist yet, it's @@ -88,11 +99,9 @@ static NSMutableString *processName = nil; convenience; other instances may also be created. */ { - static BOOL beenHere = NO; /* Flag to prevent infinite recursion */ - - if (beenHere) + if (setSharedDefaults) return sharedDefaults; - beenHere = YES; + setSharedDefaults = YES; // Create new sharedDefaults (NOTE: Not added to the autorelease pool!) sharedDefaults = [[self alloc] init]; @@ -663,10 +672,15 @@ static NSMutableString *processName = nil; } else { + NSDictionary *attr; + + attr = [NSDictionary dictionaryWithObjectsAndKeys: + NSUserName(), NSFileOwnerAccountName, nil]; NSLog(@"Creating defaults database file %@", defaultsDatabase); [[NSFileManager defaultManager] createFileAtPath: defaultsDatabase - contents: nil - attributes: nil]; + contents: nil + attributes: attr]; + [[NSDictionary dictionary] writeToFile: defaultsDatabase atomically: YES]; } if (!newDict)