Answers and Grading

CS360 Midterm Exam - March 23, 2011 - Jim Plank

Question 1

For y(), you know the following: For read10, you are simply pushing arguments onto the stack, calling procedures and getting their return values. Here they are:


y:
     ld [fp+12] -> %r0
     ld [r0] -> %r0
     mv #4 -> %r1
     add %r0, %r1 -> %r0
     ld [r0] -> %r0
     ret

read10:
      push #4       // s is at [fp]

      mov #10 -> %r0       // s = (char *) malloc(10)
      st %r0 -> [sp]--
      jsr malloc
      pop #4
      st %r0 -> [fp]
    
      mov  #10 -> %r0     // read(0, s, 10)
      st %r0 -> [sp]--
      ld [fp] -> %r0 
      st %r0 -> [sp]--
      st %g0 -> [sp]--
      jsr read
      pop #12

      ld [fp] -> %r0      // return s
      ret

Grading: 12 points

In your grades, 'y' means you got full credit, 't' means three quarters credit, 'h' means half credit in 'q' means a quarter.


Question 2

You didn't need to come up with any assembler for this one. I wanted you to show that there are three stack frames: main(), recfind(argv[1], 0) and recfind(argv[1], 6). When the program begins, you know the following things: When we call recfind(argv[1], 0), we'll push 0 and argv[1] onto the stack and we'll then make the jsr call. So: Recfind(argv[1], 0) allocates x on the stack. It's address is 0xffff482c and its value, after the strchr() call is argv[1]+5: 0xffff4881. We then make a recursive call to recfind(argv[1], 6). So: Recfind(argv[1], 6) allocates x on the stack. It's address is 0xffff4818 and its value, after the strchr() call is NULL: 0x0. At this point, we are done, and can label everything on the stack. This includes some extra stuff, because we have called strchr(s+start, ' '), so we know more information: Thus, we get the following picture:

Grading

I allotted one point for each of the following identifications:


Question 3

In the standard I/O library, reading and writing both employ an array called a buffer, which is usually 4K or 8K in size. Let's take reading as an example. The standard I/O library reads in units of the buffer size, and when the user makes calls that read, such as fread() or getchar(), the read request is serviced from the buffer. When the buffer is empty, another big read() call is made to refill it.

Buffering is useful because it eliminates the need to make small read() and write() system calls, which are expensive. In essence, the system call overhead is amortized by using memory.

Programs that perform small reads and writes are helped the most by buffering:

#include <stdio.h>

main()
{
  while (getchar() != EOF) ;
}

Programs that perform large reads and writes are not helped by buffering:

#include <stdio.h>

main()
{
  char buffer[10000];

  while (fread(buffer, 1, 10000, stdin) > 0) ;
}

A few of you said that cat is helped by buffering. I was in a kindly mood, so I didn't take off for it. If I write cat as follows:

main()
{
  char buffer[10000];
  int j;

  while (1) {
    j = read(0, buffer, 10000);
    if (j == 0) exit(0);
    write(1, buffer, j);
  }
}

It is going to work correctly, doesn't do buffering, and will run very quickly.

Grading


Question 4

Straightforward recursive directory traversal. Here's findjim.c:


/* Started:  Wed Mar 23 14:21:23 EDT 2011 */
/* Finished: Wed Mar 23 14:27:38 EDT 2011 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>
#include "dllist.h"

void findjim(char *dir)
{
  DIR *d;
  struct dirent *de;
  char *fname;
  Dllist l, tmp;
  struct stat buf;

  fname = (char *) malloc(strlen(dir)+300);
  l = new_dllist();

  d = opendir(dir);
  for (de = readdir(d); de != NULL; de = readdir(d)) {
    if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) {
      sprintf(fname, "%s/%s", dir, de->d_name);
      if (strstr(de->d_name, "jim") != NULL) printf("%s\n", fname);
      stat(fname, &buf);
      if (S_ISDIR(buf.st_mode)) dll_append(l, new_jval_s(strdup(fname)));
    }
  }
  closedir(d);
  dll_traverse(tmp, l) {
    findjim(tmp->val.s);
    free(tmp->val.s);
  }
  free_dllist(l);
  free(fname);
}

int main()
{
  findjim(".");
}

One of you tried system("grep jim *"), which gave me a laugh. The proper call is either system("find . -print | grep jim") or more properly system("find . -print | grep jim'[^/]*$").

Grading

I wasn't hugely anal in my grading. Actually, I thought that on the whole, y'all did well on this one.


Question 5

This program basically opens f1.txt for writing, and attempts to write the string "XXX\n" to it. After doing so, it performs a long listing on the file, changes its permissions to 644, and then tries to print it to standard output.

It takes three arguments:

Scenario A:
Since you've specified O_EXCL, the file is not created or opened. Nothing happens to the file:
UNIX> ls -l f1.txt
-rw-r--r--  1 plank  staff  7 Mar 23 14:10 f1.txt
UNIX> q5 xca 666 27
-rw-r--r--  1 plank  staff  7 --- -- ----- f1.txt
Shaft!
Scenario B:
The open call is made with only O_WRONLY. However, since the file exists and is writable, the open will succeed and XXX will overwrite the first four bytes of f1.txt:
UNIX> ls -l f1.txt
-rw-r--r--  1 plank  staff  7 Mar 23 14:10 f1.txt
UNIX> q5 - 777 0
-rw-r--r--  1 plank  staff  7 --- -- ----- f1.txt
XXX
t!
Scenario C:
The file will be created, and since we're calling umask(644), it will turn off protection bits 644. Thus, it will be created with 000 as permissions:
UNIX> ls -l f1.txt
ls: f1.txt: No such file or directory
UNIX> q5 ca 644 644
----------  1 plank  staff  4 --- -- ----- f1.txt
XXX
Scenario D:
I don't own the file and can't write it, but I can read it. Thus it will be unchanged.
UNIX> ls -l f1.txt
-rwxr--r--  1 binky  elves  7 Mar 23 14:10 f1.txt
UNIX> q5 a 755 272
-rwxr--r--  1 binky  elves  7 Mar 23 14:10 f1.txt
Shaft!
Scenario E:
Since the file doesn't exist, it will be created with mode 755:
UNIX> ls -l f1.txt
ls: f1.txt: No such file or directory
UNIX> q5 cx 777 22
-rwxr-xr-x  1 plank  staff  4 --- -- ----- f1.txt
XXX
Scenario F:
I don't own the file, but I do have write permissions on it, so it will be truncated before writing XXX:
UNIX> ls -l f1.txt
-rwxrwxrwx  1 binky  elves  7 Mar 23 14:10 f1.txt
UNIX> q5 ct 644 70
-rwxrwxrwx  1 binky  elves  4 --- -- ----- f1.txt
XXX
Scenario G:
It will open the file and append XXX to it:
UNIX> ls -l f1.txt
-rw-r--r--  1 plank  staff  7 Mar 23 14:10 f1.txt
UNIX> q5 ac 752 0
-rw-r--r--  1 plank  staff 11 --- -- ----- f1.txt
Shaft!
XXX
Scenario H:
I don't own the file and can't write it or read it:
UNIX> ls -l f1.txt
-rw-------  1 binky  elves  7 Mar 23 14:10 f1.txt
UNIX> q5 a 666 22
-rw-------  1 binky  elves  7 Mar 23 14:10 f1.txt
cat: f1.txt: Permission denied
Scenario I:
The file will be opened and truncated:
UNIX> ls -l f1.txt
-rw-r--r--  1 plank  staff  7 Mar 23 14:10 f1.txt
UNIX> q5 ct 706 0
-rw-r--r--  1 plank  staff  4 --- -- ----- f1.txt
XXX
Scenario J:
I did not specify O_CREAT, so nothing will happen:
UNIX> ls -l f1.txt
ls: f1.txt: No such file or directory
UNIX> q5 - 666 0
ls: f1.txt: No such file or directory
cat: f1.txt: No such file or directory
Scenario K:
It will create f1.txt with 0504 permissions:
UNIX> ls -l f1.txt
ls: f1.txt: No such file or directory
UNIX> q5 ct 504 0
-r-x---r--  1 plank  staff  4 --- -- ----- f1.txt
XXX
Scenario L:
I can't open the file, so it will remain unchanged:
UNIX> ls -l f1.txt
----r-----  1 plank  staff  7 Mar 23 14:10 f1.txt
UNIX> q5 t 766 22
----r-----  1 plank  staff  7 Mar 23 14:10 f1.txt
Shaft!

Grading

one point per part.