123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194 |
- /*++
- Copyright (c) 2015 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- shadow.c
- Abstract:
- This module implements support for manipulating the shadow password file.
- Generally applications need to have special privileges for these functions
- to succeed.
- Author:
- Evan Green 3-Mar-2015
- Environment:
- User Mode C Library
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "libcp.h"
- #include <errno.h>
- #include <fcntl.h>
- #include <shadow.h>
- #include <stdlib.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the path to the shadow lock file.
- //
- #define SHADOW_LOCK_PATH "/etc/.pwd.lock"
- //
- // Define how long to wait to acquire the lock in seconds.
- //
- #define SHADOW_LOCK_TIMEOUT 15
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- void
- ClpEmptySignalHandler (
- int Signal
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store a pointer to the shadow password file.
- //
- FILE *ClShadowFile;
- //
- // Store a pointer to the shared shadow information structure. There is always
- // a buffer after this structure that is the maximum password file line size.
- //
- struct spwd *ClShadowInformation;
- //
- // Store the file descriptor for the locked shadow password file.
- //
- int ClShadowLockDescriptor = -1;
- //
- // ------------------------------------------------------------------ Functions
- //
- LIBC_API
- struct spwd *
- getspnam (
- const char *UserName
- )
- /*++
- Routine Description:
- This routine searches the shadow password database for a user matching the
- given name, and returns information about that user's password. This
- routine is neither reentrant nor thread safe.
- Arguments:
- UserName - Supplies a pointer to the null terminated string containing the
- user name to search for.
- Return Value:
- Returns a pointer to the user password information on success. This buffer
- may be overwritten by subsequent calls to getspent or getspnam.
- NULL on failure or if the given user was not found. On failure, errno will
- be set to provide more information.
- --*/
- {
- int Result;
- struct spwd *ResultPointer;
- if (ClShadowInformation == NULL) {
- ClShadowInformation = malloc(
- sizeof(struct spwd) + USER_DATABASE_LINE_MAX);
- if (ClShadowInformation == NULL) {
- return NULL;
- }
- }
- ResultPointer = NULL;
- Result = getspnam_r(UserName,
- ClShadowInformation,
- (char *)(ClShadowInformation + 1),
- USER_DATABASE_LINE_MAX,
- &ResultPointer);
- if (Result != 0) {
- return NULL;
- }
- return ResultPointer;
- }
- LIBC_API
- int
- getspnam_r (
- const char *UserName,
- struct spwd *PasswordInformation,
- char *Buffer,
- size_t BufferSize,
- struct spwd **Result
- )
- /*++
- Routine Description:
- This routine searches the shadow password database for a user matching the
- given name, and returns information about that user's password.
- Arguments:
- UserName - Supplies a pointer to the null terminated string containing the
- user name to search for.
- PasswordInformation - Supplies a pointer where the user's password
- information will be returned.
- Buffer - Supplies a buffer used to allocate strings that the user
- information points to out of. The maximum size needed for this buffer
- can be determined with the _SC_GETPW_R_SIZE_MAX sysconf parameter.
- BufferSize - Supplies the size of the supplied buffer in bytes.
- Result - Supplies a pointer where a pointer to the user information
- parameter will be returned on success, or NULL will be returned if the
- specified user could not be found.
- Return Value:
- 0 on success.
- Returns an error value on failure.
- --*/
- {
- FILE *File;
- struct spwd Information;
- int Status;
- *Result = NULL;
- File = fopen(_PATH_SHADOW, "r");
- if (File == NULL) {
- return errno;
- }
- //
- // Loop through looking for an entry that matches.
- //
- while (TRUE) {
- Status = fgetspent_r(File, &Information, Buffer, BufferSize, Result);
- if (Status != 0) {
- *Result = NULL;
- break;
- }
- if (*Result == NULL) {
- break;
- }
- //
- // If the user name matches, return it.
- //
- if (strcmp(Information.sp_namp, UserName) == 0) {
- memcpy(PasswordInformation, &Information, sizeof(Information));
- *Result = PasswordInformation;
- break;
- }
- }
- if (File != NULL) {
- fclose(File);
- }
- return Status;
- }
- LIBC_API
- struct spwd *
- getspent (
- void
- )
- /*++
- Routine Description:
- This routine returns a pointer to the broken out fields of the next entry
- in the user database. This function is neither thread-safe nor reentrant.
- Arguments:
- None.
- Return Value:
- Returns a pointer to the next entry in the user database. The caller may
- not modify or free this memory, and it may be overwritten by subsequent
- calls to getspent.
- NULL if the end of the user database is reached or on error.
- --*/
- {
- int Result;
- struct spwd *ReturnPointer;
- if (ClShadowInformation == NULL) {
- ClShadowInformation = malloc(
- sizeof(struct spwd) + USER_DATABASE_LINE_MAX);
- if (ClShadowInformation == NULL) {
- return NULL;
- }
- }
- ReturnPointer = NULL;
- Result = getspent_r(ClShadowInformation,
- (char *)(ClShadowInformation + 1),
- USER_DATABASE_LINE_MAX,
- &ReturnPointer);
- if (Result != 0) {
- errno = Result;
- return NULL;
- }
- return ReturnPointer;
- }
- LIBC_API
- int
- getspent_r (
- struct spwd *Information,
- char *Buffer,
- size_t BufferSize,
- struct spwd **ReturnPointer
- )
- /*++
- Routine Description:
- This routine returns a pointer to the broken out fields of the next entry
- in the user password database. This is the reentrant version of getspent.
- Arguments:
- Information - Supplies a pointer where the user password information will
- be returned on success.
- Buffer - Supplies a pointer to the buffer where strings will be stored.
- BufferSize - Supplies the size of the given buffer in bytes.
- ReturnPointer - Supplies a pointer to a pointer to a user password
- information structure. On success, the information pointer parameter
- will be returned here.
- Return Value:
- 0 on success.
- ENOENT if there are no more entries.
- Returns an error value on failure.
- --*/
- {
- int Result;
- if (ClShadowFile == NULL) {
- setspent();
- }
- if (ClShadowFile == NULL) {
- return errno;
- }
- Result = fgetspent_r(ClShadowFile,
- Information,
- Buffer,
- BufferSize,
- ReturnPointer);
- return Result;
- }
- LIBC_API
- struct spwd *
- fgetspent (
- FILE *File
- )
- /*++
- Routine Description:
- This routine returns a pointer to the broken out fields of the next entry
- in the user password database.
- Arguments:
- File - Supplies a pointer to a file to read the information from.
- Return Value:
- Returns a pointer to the next entry in the user database. The caller may
- not modify or free this memory, and it may be overwritten by subsequent
- calls to getspent.
- NULL if the end of the user database is reached or on error.
- --*/
- {
- int Result;
- struct spwd *ReturnPointer;
- if (ClShadowInformation == NULL) {
- ClShadowInformation = malloc(
- sizeof(struct spwd) + USER_DATABASE_LINE_MAX);
- if (ClShadowInformation == NULL) {
- return NULL;
- }
- }
- ReturnPointer = NULL;
- Result = fgetspent_r(File,
- ClShadowInformation,
- (char *)(ClShadowInformation + 1),
- USER_DATABASE_LINE_MAX,
- &ReturnPointer);
- if (Result != 0) {
- errno = Result;
- return NULL;
- }
- return ReturnPointer;
- }
- LIBC_API
- int
- fgetspent_r (
- FILE *File,
- struct spwd *Information,
- char *Buffer,
- size_t BufferSize,
- struct spwd **ReturnPointer
- )
- /*++
- Routine Description:
- This routine returns a pointer to the broken out fields of the next entry
- in the user password database.
- Arguments:
- File - Supplies a pointer to a file to read the information from.
- Information - Supplies a pointer where the user password information will
- be returned on success.
- Buffer - Supplies a pointer to the buffer where strings will be stored.
- BufferSize - Supplies the size of the given buffer in bytes.
- ReturnPointer - Supplies a pointer to a pointer to a user password
- information structure. On success, the information pointer parameter
- will be returned here.
- Return Value:
- 0 on success.
- ENOENT if there are no more entries.
- Returns an error value on failure.
- --*/
- {
- PSTR Current;
- CHAR Line[USER_DATABASE_LINE_MAX];
- PSTR OriginalBuffer;
- size_t OriginalBufferSize;
- int Result;
- OriginalBuffer = Buffer;
- OriginalBufferSize = BufferSize;
- //
- // Loop trying to scan a good line.
- //
- *ReturnPointer = NULL;
- while (TRUE) {
- if (fgets(Line, sizeof(Line), File) == NULL) {
- if (ferror(File)) {
- return errno;
- }
- return 0;
- }
- Line[sizeof(Line) - 1] = '\0';
- Buffer = OriginalBuffer;
- BufferSize = OriginalBufferSize;
- //
- // Skip any spaces.
- //
- Current = Line;
- while (isspace(*Current) != 0) {
- Current += 1;
- }
- //
- // Skip any empty or commented lines.
- //
- if ((*Current == '\0') || (*Current == '#')) {
- continue;
- }
- Result = sgetspent_r(Line,
- Information,
- Buffer,
- BufferSize,
- ReturnPointer);
- if (Result == 0) {
- break;
- }
- }
- return 0;
- }
- LIBC_API
- struct spwd *
- sgetspent (
- const char *String
- )
- /*++
- Routine Description:
- This routine returns a pointer to the broken out fields of the next entry
- in the user database based on a shadow password file line.
- Arguments:
- String - Supplies a pointer to the shadow password line.
- Return Value:
- Returns a pointer to the next entry in the user database. The caller may
- not modify or free this memory, and it may be overwritten by subsequent
- calls to getspent.
- NULL if the end of the user database is reached or on error.
- --*/
- {
- int Result;
- struct spwd *ReturnPointer;
- if (ClShadowInformation == NULL) {
- ClShadowInformation = malloc(
- sizeof(struct spwd) + USER_DATABASE_LINE_MAX);
- if (ClShadowInformation == NULL) {
- return NULL;
- }
- }
- ReturnPointer = NULL;
- Result = sgetspent_r(String,
- ClShadowInformation,
- (char *)(ClShadowInformation + 1),
- USER_DATABASE_LINE_MAX,
- &ReturnPointer);
- if (Result != 0) {
- errno = Result;
- return NULL;
- }
- return ReturnPointer;
- }
- LIBC_API
- int
- sgetspent_r (
- const char *String,
- struct spwd *Information,
- char *Buffer,
- size_t BufferSize,
- struct spwd **ReturnPointer
- )
- /*++
- Routine Description:
- This routine returns a pointer to the broken out fields of the next entry
- in the user database based on a shadow password file line.
- Arguments:
- String - Supplies a pointer to the shadow password line.
- Information - Supplies a pointer where the user password information will
- be returned on success.
- Buffer - Supplies a pointer to the buffer where strings will be stored.
- BufferSize - Supplies the size of the given buffer in bytes.
- ReturnPointer - Supplies a pointer to a pointer to a user password
- information structure. On success, the information pointer parameter
- will be returned here.
- Return Value:
- 0 on success.
- ENOENT if there are no more entries.
- Returns an error value on failure.
- --*/
- {
- char *AfterScan;
- const char *Current;
- //
- // Loop trying to scan a good line.
- //
- *ReturnPointer = NULL;
- //
- // Skip any spaces.
- //
- Current = String;
- while (isspace(*Current) != 0) {
- Current += 1;
- }
- //
- // Skip any empty or commented lines.
- //
- if ((*Current == '\0') || (*Current == '#')) {
- return EINVAL;
- }
- //
- // Grab the username. Skip malformed lines.
- //
- Information->sp_namp = Buffer;
- while ((BufferSize != 0) && (*Current != '\0') && (*Current != ':')) {
- *Buffer = *Current;
- Buffer += 1;
- Current += 1;
- BufferSize -= 1;
- }
- if (BufferSize != 0) {
- *Buffer = '\0';
- Buffer += 1;
- }
- if (*Current != ':') {
- return EINVAL;
- }
- Current += 1;
- //
- // Grab the password.
- //
- Information->sp_pwdp = Buffer;
- while ((BufferSize != 0) && (*Current != '\0') && (*Current != ':')) {
- *Buffer = *Current;
- Buffer += 1;
- Current += 1;
- BufferSize -= 1;
- }
- if (BufferSize != 0) {
- *Buffer = '\0';
- Buffer += 1;
- }
- if (*Current != ':') {
- return EINVAL;
- }
- Current += 1;
- //
- // Grab the last changed days.
- //
- Information->sp_lstchg = strtol(Current, &AfterScan, 10);
- if (AfterScan == Current) {
- Information->sp_lstchg = -1;
- }
- Current = AfterScan;
- if (*Current != ':') {
- return EINVAL;
- }
- Current += 1;
- //
- // Grab the number of days before the password may be changed.
- //
- Information->sp_min = strtoul(Current, &AfterScan, 10);
- if (AfterScan == Current) {
- Information->sp_min = -1;
- }
- Current = AfterScan;
- if (*Current != ':') {
- return EINVAL;
- }
- Current += 1;
- //
- // Grab the number of days before the password must be changed.
- //
- Information->sp_max = strtoul(Current, &AfterScan, 10);
- if (AfterScan == Current) {
- Information->sp_max = -1;
- }
- Current = AfterScan;
- if (*Current != ':') {
- return EINVAL;
- }
- Current += 1;
- //
- // Grab the number of days before warning that the password should be
- // changed.
- //
- Information->sp_warn = strtoul(Current, &AfterScan, 10);
- if (AfterScan == Current) {
- Information->sp_warn = -1;
- }
- Current = AfterScan;
- if (*Current != ':') {
- return EINVAL;
- }
- Current += 1;
- //
- // Grab the number of days after the password expires that the account is
- // disabled.
- //
- Information->sp_inact = strtoul(Current, &AfterScan, 10);
- if (AfterScan == Current) {
- Information->sp_inact = -1;
- }
- Current = AfterScan;
- if (*Current != ':') {
- return EINVAL;
- }
- Current += 1;
- //
- // Grab the number of days since 1970 that an account has been disabled.
- //
- Information->sp_expire = strtoul(Current, &AfterScan, 10);
- if (AfterScan == Current) {
- Information->sp_expire = -1;
- }
- Current = AfterScan;
- if (*Current != ':') {
- return EINVAL;
- }
- Current += 1;
- //
- // Grab the reserved flags.
- //
- Information->sp_flag = strtoul(Current, &AfterScan, 0);
- if (AfterScan == Current) {
- Information->sp_flag = -1;
- }
- *ReturnPointer = Information;
- return 0;
- }
- LIBC_API
- void
- setspent (
- void
- )
- /*++
- Routine Description:
- This routine rewinds the user password shadow database handle back to the
- beginning of the database. The next call to getspent will return the first
- entry in the user database.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- if (ClShadowFile == NULL) {
- ClShadowFile = fopen(_PATH_SHADOW, "r");
- } else {
- fseek(ClShadowFile, 0, SEEK_SET);
- }
- return;
- }
- LIBC_API
- void
- endspent (
- void
- )
- /*++
- Routine Description:
- This routine closes an open handle to the user password database
- established with setspent or getspent.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- if (ClShadowFile != NULL) {
- fclose(ClShadowFile);
- ClShadowFile = NULL;
- }
- return;
- }
- LIBC_API
- int
- putspent (
- const struct spwd *Information,
- FILE *Stream
- )
- /*++
- Routine Description:
- This routine writes a shadow password entry into the given shadow password
- database file stream. This routine is neither thread safe nor reentrant.
- Arguments:
- Information - Supplies a pointer to the password information to write.
- Stream - Supplies a pointer to the file stream to write it out to.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to contain more information.
- --*/
- {
- int FinalResult;
- flockfile(Stream);
- FinalResult = 0;
- if (fprintf(Stream, "%s:", Information->sp_namp) < 0) {
- FinalResult = -1;
- }
- if (Information->sp_pwdp != NULL) {
- if (fprintf(Stream, "%s:", Information->sp_pwdp) < 0) {
- FinalResult = -1;
- }
- } else {
- if (fputc(':', Stream) == EOF) {
- FinalResult = -1;
- }
- }
- if (Information->sp_min != (long int)-1) {
- if (fprintf(Stream, "%ld:", Information->sp_min) < 0) {
- FinalResult = -1;
- }
- } else {
- if (fputc(':', Stream) == EOF) {
- FinalResult = -1;
- }
- }
- if (Information->sp_max != (long int)-1) {
- if (fprintf(Stream, "%ld:", Information->sp_max) < 0) {
- FinalResult = -1;
- }
- } else {
- if (fputc(':', Stream) == EOF) {
- FinalResult = -1;
- }
- }
- if (Information->sp_warn != (long int)-1) {
- if (fprintf(Stream, "%ld:", Information->sp_warn) < 0) {
- FinalResult = -1;
- }
- } else {
- if (fputc(':', Stream) == EOF) {
- FinalResult = -1;
- }
- }
- if (Information->sp_inact != (long int)-1) {
- if (fprintf(Stream, "%ld:", Information->sp_inact) < 0) {
- FinalResult = -1;
- }
- } else {
- if (fputc(':', Stream) == EOF) {
- FinalResult = -1;
- }
- }
- if (Information->sp_expire != (long int)-1) {
- if (fprintf(Stream, "%ld:", Information->sp_expire) < 0) {
- FinalResult = -1;
- }
- } else {
- if (fputc(':', Stream) == EOF) {
- FinalResult = -1;
- }
- }
- if (Information->sp_flag != (unsigned long int)-1) {
- if (fprintf(Stream, "%lu", Information->sp_flag) < 0) {
- FinalResult = -1;
- }
- }
- if (fputc('\n', Stream) == EOF) {
- FinalResult = -1;
- }
- funlockfile(Stream);
- return FinalResult;
- }
- LIBC_API
- int
- lckpwdf (
- void
- )
- /*++
- Routine Description:
- This routine locks the shadow password database file. This routine is not
- multi-thread safe. This mechanism does not prevent someone from writing
- directly to the shadow password file, as the locks it uses are
- discretionary.
- Arguments:
- None.
- Return Value:
- 0 on success.
- -1 if the lock could not be acquired within 15 seconds, and errno may be
- set to contain more information.
- --*/
- {
- struct sigaction AlarmAction;
- BOOL AlarmActionSet;
- int Flags;
- struct flock Lock;
- sigset_t Mask;
- int OpenFlags;
- struct sigaction OriginalAlarmAction;
- sigset_t OriginalMask;
- int Result;
- AlarmActionSet = FALSE;
- //
- // If the lock is already held by this process, fail.
- //
- if (ClShadowLockDescriptor != -1) {
- return -1;
- }
- //
- // TODO: Add O_CLOEXEC to this.
- //
- OpenFlags = O_WRONLY | O_CREAT;
- ClShadowLockDescriptor = open(SHADOW_LOCK_PATH,
- OpenFlags,
- S_IRUSR | S_IWUSR);
- if (ClShadowLockDescriptor < 0) {
- return -1;
- }
- Flags = fcntl(ClShadowLockDescriptor, F_GETFD, 0);
- if (Flags == -1) {
- Result = -1;
- goto lckpwdfEnd;
- }
- Flags |= FD_CLOEXEC;
- Result = fcntl(ClShadowLockDescriptor, F_SETFD, Flags);
- if (Result < 0) {
- goto lckpwdfEnd;
- }
- //
- // Set an alarm handler so the signal comes in.
- //
- memset(&AlarmAction, 0, sizeof(AlarmAction));
- AlarmAction.sa_handler = ClpEmptySignalHandler;
- sigfillset(&(AlarmAction.sa_mask));
- AlarmAction.sa_flags = 0;
- Result = sigaction(SIGALRM, &AlarmAction, &OriginalAlarmAction);
- if (Result < 0) {
- goto lckpwdfEnd;
- }
- AlarmActionSet = TRUE;
- //
- // Make sure alarm is not blocked.
- //
- sigemptyset(&Mask);
- sigaddset(&Mask, SIGALRM);
- Result = sigprocmask(SIG_UNBLOCK, &Mask, &OriginalMask);
- if (Result < 0) {
- goto lckpwdfEnd;
- }
- //
- // Start a timer.
- //
- alarm(SHADOW_LOCK_TIMEOUT);
- //
- // Acquire the lock.
- //
- memset(&Lock, 0, sizeof(Lock));
- Lock.l_type = F_WRLCK;
- Lock.l_whence = SEEK_SET;
- Result = fcntl(ClShadowLockDescriptor, F_SETLKW, &Lock);
- //
- // Restore the mask.
- //
- sigprocmask(SIG_SETMASK, &OriginalMask, NULL);
- lckpwdfEnd:
- if (AlarmActionSet != FALSE) {
- sigaction(SIGALRM, &OriginalAlarmAction, NULL);
- }
- if (Result != 0) {
- if (ClShadowLockDescriptor >= 0) {
- close(ClShadowLockDescriptor);
- ClShadowLockDescriptor = -1;
- }
- }
- return Result;
- }
- LIBC_API
- int
- ulckpwdf (
- void
- )
- /*++
- Routine Description:
- This routine unlocks the shadow password file.
- Arguments:
- None.
- Return Value:
- 0 on success.
- -1 on failure.
- --*/
- {
- int Result;
- //
- // If there is no descriptor, the caller's trying to unlock something
- // they never locked.
- //
- if (ClShadowLockDescriptor < 0) {
- return -1;
- }
- Result = close(ClShadowLockDescriptor);
- ClShadowLockDescriptor = -1;
- return Result;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- void
- ClpEmptySignalHandler (
- int Signal
- )
- /*++
- Routine Description:
- This routine implements an empty signal handler. This is needed when it is
- desired that a certain signal interrupt an operation, but that signal's
- default action would be to terminate.
- Arguments:
- Signal - Supplies the signal number that occurred. This is ignored.
- Return Value:
- None.
- --*/
- {
- return;
- }
|