// file_delete(char *filename) int TrapFileDeleteHandler(uint32 *trapArgs, int sysMode) { char filename[FILE_MAX_FILENAME_LENGTH]; char *user_filename = NULL; int i; // If we're not in system mode, we need to copy everything from the // user-space virtual address to the kernel space address if (!sysMode) { // Get the arguments themselves into system space // Argument 0: userland pointer to filename string MemoryCopyUserToSystem (currentPCB, (trapArgs+0), &user_filename, sizeof(uint32)); // Now copy userland filename string into our filename buffer for(i=0; i<FILE_MAX_FILENAME_LENGTH; i++) { MemoryCopyUserToSystem(currentPCB, (user_filename+i), &(filename[i]), sizeof(char)); // Check for end of user-space string if (filename[i] == '\0') break; } if (i == FILE_MAX_FILENAME_LENGTH) { printf("TrapFileDeleteHandler: length of filename longer than allowed!\n"); GracefulExit(); } dbprintf('F', "TrapDeleteCloseHandler: just parsed filename (%s) from trapArgs\n", filename); } else { // Already in kernel space, no address translation necessary dstrncpy(filename, (char *)(trapArgs[0]), FILE_MAX_FILENAME_LENGTH); } return FileDelete(filename); }
//----------------------------------------------------------------- // DfsInodeOpen: search the list of all inuse inodes for the // specified filename. If the filename exists, return the handle // of the inode. If it does not, allocate a new inode for this // filename and return its handle. Return DFS_FAIL on failure. // Remember to use locks whenever you allocate a new inode. //----------------------------------------------------------------- uint32 DfsInodeOpen(char *filename) { uint32 i; if(!sb.valid) { printf("DfsInodeOpen: invalid file system\n"); return DFS_FAIL; } i = DfsInodeFilenameExists(filename); if(i != DFS_FAIL) { printf("[DEBUG] Opened inode:%d\n", i); return i; } while(LockHandleAcquire(dfs_inode_lock) != SYNC_SUCCESS); for(i = 0; i < sb.total_inodes; i++) { if(inodes[i].valid) { continue; } inodes[i].valid = 1; inodes[i].filesize = 0; dstrncpy(inodes[i].filename, filename, DFS_FILENAME_LENGTH); break; } LockHandleRelease(dfs_inode_lock); printf("[DEBUG] Opened inode:%d\n", i); return i; }
static void TrapProcessCreateHandler(uint32 *trapArgs, int sysmode) { char allargs[SIZE_ARG_BUFF]; char name[100]; int i=0, j=0, k=0; uint32 args[MAX_ARGS]; char *destination; // The first argument is the name of the executable file i=0; for(i=0;i<100; i++) allargs[i] = 0; i=0; if(!sysmode) { //Get the arguments into the sytem space MemoryCopyUserToSystem (currentPCB, trapArgs, args, sizeof (args)); do { MemoryCopyUserToSystem (currentPCB,((char*)args[0])+i,name+i,1); i++; } while ((i < sizeof (name)) && (name[i-1] != '\0')); } else { bcopy (trapArgs, args, sizeof (args)); dstrncpy ((char *)args[0], name, sizeof (name)); } name[sizeof(name)-1] = '\0'; // null terminate the name i=0; if(!sysmode) { //Copy the rest of the arguments to the system space for(j=0; (j<11)&&(args[j]!=0); j++) { k=0; do { MemoryCopyUserToSystem (currentPCB,((char*)args[j])+k,allargs+i,1); i++; k++; } while ((i<sizeof(allargs)) && (allargs[i-1]!='\0')); } } else { destination = &allargs[0]; for(j=0; (j<11)&&(args[j]!=0); j++) { k = dstrlen((char *)args[j]); //length of the argument if(&destination[k]-allargs>100) { printf("Fatal: Cumulative length of all arguments > 100\n"); exitsim(); } dstrcpy(destination, (char *)args[j]); destination[k] = '\0'; } } allargs[sizeof(allargs)-1] = '\0'; // null terminate the name ProcessFork(0, (uint32)allargs, name, 1); }
/* * Replace the first occurrence of 'find' after the index 'start' with 'repl' * Returns the position right after the replaced string */ int dstrreplace(dstr_t *ds, const char *find, const char *repl, int start) { char *p; dstr_t *copy = create_dstr(); int end = -1; dstrcpy(copy, ds->data); if ((p = strstr(©->data[start], find))) { dstrncpy(ds, copy->data, p - copy->data); dstrcatf(ds, "%s", repl); end = ds->len; dstrcatf(ds, "%s", p + strlen(find)); } free_dstr(copy); return end; }
//-------------------------------------------------------------------- // // int process_create(char *exec_name, ...); // // Here we support reading command-line arguments. Maximum MAX_ARGS // command-line arguments are allowed. Also the total length of the // arguments including the terminating '\0' should be less than or // equal to SIZE_ARG_BUFF. // //-------------------------------------------------------------------- static void TrapProcessCreateHandler(uint32 *trapArgs, int sysmode) { char allargs[SIZE_ARG_BUFF]; // Stores full string of arguments (unparsed) char name[PROCESS_MAX_NAME_LENGTH]; // Local copy of name of executable (100 chars or less) char *username=NULL; // Pointer to user-space address of exec_name string int i=0, j=0; // Loop index variables char *args[MAX_ARGS]; // All parsed arguments (char *'s) int allargs_position = 0; // Index into current "position" in allargs char *userarg = NULL; // Current pointer to user argument string int numargs = 0; // Number of arguments passed on command line dbprintf('p', "TrapProcessCreateHandler: function started\n"); // Initialize allargs string to all NULL's for safety for(i=0;i<SIZE_ARG_BUFF; i++) { allargs[i] = '\0'; } // First deal with user-space addresses if(!sysmode) { dbprintf('p', "TrapProcessCreateHandler: creating user process\n"); // Get the known arguments into the kernel space. // Argument 0: user-space pointer to name of executable MemoryCopyUserToSystem (currentPCB, (char *)(trapArgs+0), (char *)&username, sizeof(char *)); // Copy the user-space string at user-address "username" into kernel space for(i=0; i<PROCESS_MAX_NAME_LENGTH; i++) { MemoryCopyUserToSystem(currentPCB, (char *)(username+i), (char *)&(name[i]), sizeof(char)); // Check for end of user-space string if (name[i] == '\0') break; } dbprintf('p', "TrapProcessCreateHandler: just parsed executable name (%s) from trapArgs\n", name); if (i == PROCESS_MAX_NAME_LENGTH) { printf("TrapProcessCreateHandler: length of executable filename longer than allowed!\n"); exitsim(); } // Copy the program name into "allargs", since it has to be the first argument (i.e. argv[0]) allargs_position = 0; dstrcpy(&(allargs[allargs_position]), name); allargs_position += dstrlen(name) + 1; // The "+1" is so we're pointing just beyond the NULL // Rest of arguments: a series of char *'s until we hit NULL or MAX_ARGS for(i=0; i<MAX_ARGS; i++) { // First, must copy the char * itself into kernel space in order to read its value MemoryCopyUserToSystem(currentPCB, (char *)(trapArgs+1+i), (char *)&userarg, sizeof(char *)); // If this is a NULL in the set of char *'s, this is the end of the list if (userarg == NULL) break; // Store a pointer to the kernel-space location where we're copying the string args[i] = &(allargs[allargs_position]); // Copy the string into the allargs, starting where we left off last time through this loop for (j=0; j<SIZE_ARG_BUFF; j++) { MemoryCopyUserToSystem(currentPCB, (char *)(userarg+j), (char *)&(allargs[allargs_position]), sizeof(char)); // Move current character in allargs to next spot allargs_position++; // Check that total length of arguments is still ok if (allargs_position == SIZE_ARG_BUFF) { printf("TrapProcessCreateHandler: strlen(all arguments) > maximum length allowed!\n"); exitsim(); } // Check for end of user-space string if (allargs[allargs_position-1] == '\0') break; } } if (i == MAX_ARGS) { printf("TrapProcessCreateHandler: too many arguments on command line (did you forget to pass a NULL?)\n"); exitsim(); } numargs = i+1; // Arguments are now setup } else { // Addresses are already in kernel space, so just copy into our local variables // for simplicity // Argument 0: (char *) name of program dstrncpy(name, (char *)(trapArgs[0]), PROCESS_MAX_NAME_LENGTH); // Copy the program name into "allargs", since it has to be the first argument (i.e. argv[0]) allargs_position = 0; dstrcpy(&(allargs[allargs_position]), name); allargs_position += dstrlen(name) + 1; // The "+1" is so that we are now pointing just beyond the "null" in the name allargs_position = 0; for (i=0; i<MAX_ARGS; i++) { userarg = (char *)(trapArgs[i+1]); if (userarg == NULL) break; // found last argument // Store the address of where we're copying the string args[i] = &(allargs[allargs_position]); // Copy string into allargs for(j=0; j<SIZE_ARG_BUFF; j++) { allargs[allargs_position] = userarg[j]; allargs_position++; if (allargs_position == SIZE_ARG_BUFF) { printf("TrapProcessCreateHandler: strlen(all arguments) > maximum length allowed!\n"); exitsim(); } // Check for end of user-space string if (allargs[allargs_position-1] == '\0') break; } } if (i == MAX_ARGS) { printf("TrapProcessCreateHandler: too many arguments on command line (did you forget to pass a NULL?)\n"); exitsim(); } numargs = i+1; } ProcessFork(0, (uint32)allargs, name, 1); }
//---------------------------------------------------------------------- // // TrapPrintfHandler // // Handle a printf trap.here. // Note that the maximum format string length is PRINTF_MAX_FORMAT_LENGTH // characters. Exceeding this length could have unexpected results. // //---------------------------------------------------------------------- static void TrapPrintfHandler(uint32 *trapArgs, int sysMode) { char formatstr[PRINTF_MAX_FORMAT_LENGTH]; // Copy user format string here char *userformatstr=NULL; // Holds user-space address of format string int i,j; // Loop index variables int numargs=0; // Keeps track of number of %'s (i.e. number of arguments) uint32 args[PRINTF_MAX_ARGS]; // Keeps track of all our args char *userstr; // Holds user-space address of the string that goes with any "%s"'s char strings_storage[PRINTF_MAX_STRING_ARGS][PRINTF_MAX_STRING_ARG_LENGTH]; // Places to copy strings for "%s" into int string_argnum=0; // Keeps track of how many "%s"'s we have received (index into strings_storage) if (!sysMode) { // Copy user-space format string into kernel space // Argument 0: format string MemoryCopyUserToSystem(currentPCB, (char *)(trapArgs+0), (char *)&userformatstr, sizeof(char *)); // Copy format string itself from user space to kernel space for(i=0; i<PRINTF_MAX_FORMAT_LENGTH; i++) { MemoryCopyUserToSystem(currentPCB, (char *)(userformatstr + i), (char *)&(formatstr[i]), sizeof(char)); // Check for end of string if (formatstr[i] == '\0') break; } if (i == PRINTF_MAX_FORMAT_LENGTH) { // format string too long printf("TrapPrintfHandler: format string too long!\n"); return; } } else { // Not in system mode, can just copy string directly dstrncpy(formatstr, (char *)(trapArgs+0), PRINTF_MAX_FORMAT_LENGTH); } // Now read format string to find all the %'s numargs = 0; string_argnum = 0; for(i=0; i<dstrlen(formatstr); i++) { if (formatstr[i] == '%') { // Check for "%%" i++; // Skip over % symbol to look at the next character switch(formatstr[i]) { case '%': continue; // Skip over "%%" break; case 'c': // chars // This argument is a single char value, but I think it's stored as a full int value. // If it isn't, then I'm not sure how to call the *real* printf below with some non-4-byte arguments. // The "+1" is because trapArgs[0] is the format string if (!sysMode) { MemoryCopyUserToSystem(currentPCB, (char *)(trapArgs+numargs+1), (char *)&(args[numargs]), sizeof(int)); } else { args[numargs] = trapArgs[numargs+1]; } numargs++; break; case 'l': if (formatstr[i+1] != 'f') { printf("TrapPrintfHandler: unrecognized format character (l%c)\n", formatstr[i+1]); return; } i++; // There is intentionally no "break" here so that the %lf and %f cases do the same thing. // This is what the previous Printf did, but I'm not sure yet that it is right. case 'f': // double floating points (64 bits) case 'g': case 'e': if (!sysMode) { MemoryCopyUserToSystem(currentPCB, (char *)(trapArgs+numargs+1), (char *)&(args[numargs]), sizeof(double)); } else { args[numargs] = trapArgs[numargs+1]; args[numargs+1] = trapArgs[numargs+2]; } numargs += 2; // numargs is incremented by 2 since a double is the size of 2 ints break; case 'd': // integers if (!sysMode) { MemoryCopyUserToSystem(currentPCB, (char *)(trapArgs+numargs+1), (char *)&(args[numargs]), sizeof(int)); } else { args[numargs] = trapArgs[numargs+1]; } numargs++; break; case 's': // string if (!sysMode) { // First make sure we still have local string storage space available if (string_argnum == PRINTF_MAX_STRING_ARGS) { printf("TrapPrintfHandler: too many %%s arguments passed!\n"); return; } // Now copy the user-space address of the string into kernel space MemoryCopyUserToSystem(currentPCB, (char *)(trapArgs+numargs+1), (char *)&userstr, sizeof(char *)); // Now copy each individual character from that string into kernel space for(j=0; j<PRINTF_MAX_STRING_ARG_LENGTH; j++) { MemoryCopyUserToSystem(currentPCB, (char *)(userstr+j), (char *)&(strings_storage[string_argnum][j]), sizeof(char)); // Check for end of user-space string if (strings_storage[string_argnum][j] == '\0') break; } if (j == PRINTF_MAX_STRING_ARG_LENGTH) { // String was too long! printf("TrapPrintfHandler: argument #%d (a string) was too long to print!\n", numargs); return; } // Put the address of this kernel-space string as the argument for printf args[numargs] = (int)(strings_storage[string_argnum]); string_argnum++; } else { // kernel space already, just copy address out of trapArgs args[numargs] = trapArgs[numargs+1]; } numargs++; break; default: printf("TrapPrintfHandler: unrecognized format character (%c)!\n", formatstr[i+1]); return; } } } // Now we can print them all out switch(numargs) { case 0: printf(formatstr); break; case 1: printf(formatstr, args[0]); break; case 2: printf(formatstr, args[0], args[1]); break; case 3: printf(formatstr, args[0], args[1], args[2]); break; case 4: printf(formatstr, args[0], args[1], args[2], args[3]); break; case 5: printf(formatstr, args[0], args[1], args[2], args[3], args[4]); break; case 6: printf(formatstr, args[0], args[1], args[2], args[3], args[4], args[5]); break; case 7: printf(formatstr, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); break; case 8: printf(formatstr, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break; case 9: printf(formatstr, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); break; case 10: printf(formatstr, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); break; case 11: printf(formatstr, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); break; case 12: printf(formatstr, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]); break; default: printf("TrapPrintfHandler: too many arguments (%d) passed!\n", numargs); } // Done! }
//---------------------------------------------------------------------- // // TrapPrintfHandler // // Handle a printf trap.here. This printf code will correctly // handle integers but will not correctly handle strings (yet). // Also, note that the maximum format string length is 79 characters. // Exceeding this length will cause the format to be truncated. // //---------------------------------------------------------------------- static void TrapPrintfHandler (uint32 *trapArgs, int sysMode) { char formatstr[80]; int i = 0; uint32 printfArgs[10]; uint32 args[10]; int nargs = 0; char *c; // The first argument is the print format string. Copy it to system // space, truncating if necessary. i = 0; if (!sysMode) { // Get the arguments themselves into system space MemoryCopyUserToSystem (currentPCB, trapArgs, args, sizeof (args)); do { MemoryCopyUserToSystem (currentPCB,((char*)args[0])+i,formatstr+i,1); i++; } while ((i < sizeof (formatstr)) && (formatstr[i-1] != '\0')); } else { bcopy (trapArgs, args, sizeof (args)); dstrncpy ((char *)args[0], formatstr, sizeof (formatstr)); } formatstr[sizeof(formatstr)-1] = '\0'; // null terminate the fmt str for (c = formatstr; *c != '\0'; c++) { if (*c == '%') { // if this is a %%, skip past second % if (*(c+1) == '%') { c++; continue; } // Get the current argument off the stack printfArgs[nargs] = args[nargs+1]; // dbprintf ('t', "Argument %d at 0x%x is %d (0x%x).\n", nargs, // args[nargs], args[nargs]); while (1) { c++; if (*c == 's') { // Handle strings here. They don't work for user programs (yet...) break; } else if (*c == 'l') { continue; } else if ((*c == 'f') || (*c == 'g') || (*c == 'e')) { // If it's a floating point number, it'll be passed as // a double, so grab the second word also. nargs += 1; printfArgs[nargs] = args[nargs+1]; break; } else if ((*c >= 'a') && (*c <= 'z')) { // If it's another formatting character, it's not // a string, but we can leave the loop anyway. break; } } nargs += 1; } } printf (formatstr,printfArgs[0],printfArgs[1],printfArgs[2], printfArgs[3], printfArgs[4], printfArgs[5], printfArgs[6], printfArgs[7]); }
//------------------------------------------------------- // // int MboxRecv(mbox_t handle, int maxlength, void* message); // // Receive a message from the specified mailbox. The call // blocks when there is no message in the buffer. Maxlength // should indicate the maximum number of bytes that can be // copied from the buffer into the address of "message". // An error occurs if the message is larger than maxlength. // Note that the calling process must have opened the mailbox // via MboxOpen. // // Returns MBOX_FAIL on failure. // Returns number of bytes written into message on success. // //------------------------------------------------------- int MboxRecv(mbox_t handle, int maxlength, void* message) { int i; int qlen; Link *l; int exists = 0; mbox_message *m; //check if mailbox is real if(!&mboxs[handle]) return MBOX_FAIL; //check if pid opened this mailbox if(!AQueueEmpty(&mboxs[handle].pids ) ){ qlen = AQueueLength(&mboxs[handle].pids); l = AQueueFirst(&mboxs[handle].pids); for(i=0; i < qlen; i++){ if((int)AQueueObject(l) == GetCurrentPid()){ exists = 1; break; } l = AQueueNext(l); } //actualy checks if pid exists if(exists == 0){ return MBOX_FAIL; } //wait until queue has something in it if(SemHandleWait(mboxs[handle].s_msg_full) == SYNC_FAIL){ printf("bad sem handle wait in mbox recv\n"); exitsim(); } //lock if(LockHandleAcquire(mboxs[handle].l) != SYNC_SUCCESS){ printf("FATAL ERROR: could not get lock in Mbox send!\n"); exitsim(); } l = AQueueFirst(&mboxs[handle].messages); m = (mbox_message *) l->object; //check if message longer than max length if(m->length > maxlength) { return MBOX_FAIL; } //copy message to local variable dstrncpy(message,(void*) m->message, m->length); //reset structure for use elsewhere m->inuse =0; //delete link AQueueRemove(&l); //unlock if(LockHandleRelease(mboxs[handle].l) != SYNC_SUCCESS){ printf("FATAL ERROR: could not release lock in Mbox send!\n"); exitsim(); } if(SemHandleSignal(mboxs[handle].s_msg_empty) == SYNC_FAIL){ printf("bad sem handle signal in mbox recv\n"); exitsim(); } }else{ return MBOX_FAIL; } return MBOX_SUCCESS; }
//------------------------------------------------------- // // int MboxSend(mbox_t handle,int length, void* message); // // Send a message (pointed to by "message") of length // "length" bytes to the specified mailbox. Messages of // length 0 are allowed. The call // blocks when there is not enough space in the mailbox. // Messages cannot be longer than MBOX_MAX_MESSAGE_LENGTH. // Note that the calling process must have opened the // mailbox via MboxOpen. // // Returns MBOX_FAIL on failure. // Returns MBOX_SUCCESS on success. // //------------------------------------------------------- int MboxSend(mbox_t handle, int length, void* message) { int i; int qlen; Link *l; int exists = 0; //check if mailbox is real if(!&mboxs[handle]) return MBOX_FAIL; //check if pid opened this mailbox if(!AQueueEmpty(&mboxs[handle].pids ) ){ qlen = AQueueLength(&mboxs[handle].pids); l = AQueueFirst(&mboxs[handle].pids); for(i=0; i < qlen; i++){ if((int)AQueueObject(l) == GetCurrentPid()){ exists = 1; break; } l = AQueueNext(l); } //actuall checks if pid exists if(exists == 0){ return MBOX_FAIL; } //check if message longer than max length if(length > MBOX_MAX_MESSAGE_LENGTH) { return MBOX_FAIL; } //check for space in mailbox if((SemHandleWait(mboxs[handle].s_msg_empty)) == SYNC_FAIL ){ printf("bad sem handle wait in mbox send\n"); exitsim(); } //lock if(LockHandleAcquire(mboxs[handle].l) != SYNC_SUCCESS){ printf("FATAL ERROR: could not get lock in Mbox send!\n"); exitsim(); } for(i=0; i<MBOX_NUM_BUFFERS; i++){ if(mbox_messages[i].inuse == 0){ mbox_messages[i].inuse = 1; break; } } //creating mbox_message structure dstrncpy(mbox_messages[i].message, (char *) message, length); mbox_messages[i].length = length; if ((l = AQueueAllocLink(&mbox_messages[i])) == NULL) { printf("FATAL ERROR: could not allocate link for pid queue in Mbox Open!\n"); exitsim(); } //add message to end of queue AQueueInsertLast(&mboxs[handle].messages, l); //unlock if(LockHandleRelease(mboxs[handle].l) != SYNC_SUCCESS){ printf("FATAL ERROR: could not release lock in Mbox send!\n"); exitsim(); } if(SemHandleSignal(mboxs[handle].s_msg_full) == SYNC_FAIL){ printf("bad sem handle signal in mbox send\n"); exitsim(); } }else{ return MBOX_FAIL; } return MBOX_SUCCESS; }
int FileOpen(char *filename, char *mode){ int inode_handle; int i; if(dstrlen(filename) > FILE_MAX_FILENAME_LENGTH) { printf("FileOpen -- Filename is too large!\n"); return(FILE_FAIL); } if((dstrlen(mode) == 2) && (*mode == 'r') && (*(mode+1) == 'w')) { printf("RW MODE\n"); // get inode_handle (create if doesn't exist) inode_handle = DfsInodeOpen(filename); if(inode_handle == -1) { printf("FileOpen -- Unable to open file in read/write mode\n"); return(FILE_FAIL); } } else if((dstrlen(mode) == 1) && (*mode == 'r')) { printf("R MODE\n"); // get inode_handle (create if doesn't exist) inode_handle = DfsInodeOpen(filename); if(inode_handle == -1) { printf("FileOpen -- Unable to open file in read mode\n"); return(FILE_FAIL); } } else if((dstrlen(mode) == 1) && (*mode == 'w')) { printf("W MODE\n"); // check if exists inode_handle = DfsInodeFilenameExists(filename); if(inode_handle != -1) // if it exists, delete { dbprintf('F', "FileOpen -- Opening in W mode and file exists... deleting inode!\n"); if(DfsInodeDelete(inode_handle) == -1) { printf("FileOpen -- Unable to delete inode\n"); return(FILE_FAIL); } // TODO: invalidate all file descriptors corresponding to the deleted inode. } // then open inode_handle = DfsInodeOpen(filename); if(inode_handle == -1) { printf("FileOpen -- Unable to open file in read mode\n"); return(FILE_FAIL); } } else { printf("FileOpen -- Invalid mode!\n"); return(FILE_FAIL); } // We now have an inode descriptor for a given file // Find an available file descriptor to hold data for(i=0; i<FILE_MAX_OPEN_FILES; i++) { if(fd_array[i].FD_Valid == 0) { // Populate file descriptor with appropriate data dstrncpy ((char*)&fd_array[i].FD_FileName, filename, FILE_MAX_FILENAME_LENGTH); fd_array[i].FD_InodeHandle = inode_handle; fd_array[i].FD_CurrentPosition = 0; fd_array[i].FD_EOF_Flag = 0; if(dstrlen(mode) == 2) fd_array[i].FD_Mode = FILE_READWRITE; else if((dstrlen(mode) == 1) && (*mode == 'r')) fd_array[i].FD_Mode = FILE_READ; else fd_array[i].FD_Mode = FILE_WRITE; fd_array[i].FD_PID = GetCurrentPid(); // mark as valid, and return handle to file descriptor fd_array[i].FD_Valid = 1; dbprintf('F', "FileOpen -- Using file descriptor #%d for newly opened file\n", i); return(i); } } printf("FileOpen -- Unable to find an empty file descriptor!\n"); return(FILE_FAIL); }