Operating Systems 2019W: Assignment 2: Difference between revisions

From Soma-notes
No edit summary
No edit summary
 
(8 intermediate revisions by the same user not shown)
Line 1: Line 1:
'''This assignment is not yet finalized.  Please do not answer these questions yet, they are subject to change.'''
Please submit the answers to the following questions via CULearn by 4 PM on Monday, February 11, 2019. There are 20 points in 9 questions (and 4 points extra credit).
Please submit the answers to the following questions via CULearn by 4 PM on Monday, February 11, 2019. There are 20 points in 9 questions (and 4 points extra credit).


Line 11: Line 9:
==Questions==
==Questions==


# [2] Assume you have a file A.  You type <tt>ln A B</tt>  in order to create file B.  If you delete A, what happens when you try to read A?  Why?
# [2] Assume you have a file A.  You type <tt>ln A B</tt>  in order to create file B.  If you delete A, what happens when you try to read B?  Why?
# [2] Assume you have a file A.  You type <tt>ln -s A B</tt> in order to create file B.  If you delete A, what happens when you try to read A?  Why?
# [2] Assume you have a file A.  You type <tt>ln -s A B</tt> in order to create file B.  If you delete A, what happens when you try to read B?  Why?
# [2] What does fsck do if it finds an allocated inode that no file refers to?  How could this problem arise?
# [2] What does fsck do if it finds an allocated inode that no file refers to?  How could this problem arise?
# [2] Describe a situation where a UNIX sparse file could cause problems for a backup program (that didn't specially treat sparse files).
# [2] Describe a situation where a UNIX sparse file could cause problems for a backup program (that didn't specially treat sparse files).
# [3] Modify [https://homeostasis.scs.carleton.ca/~soma/os-2017f/code/tut3/3000test.c 3000test.c] so it can report on what access the current user has to the file.  Give your code as a "diff -c" relative to the original 3000test.c.
# [3] Create a program 3000run.c (based on 3000test.c) that attempts to run the contents of a file by mmap'ing it into an array, converts the array pointer into a function pointer for a function that takes one argument, a standard C string (char *) and then calls the function, passing it a string.  Test your code on [https://homeostasis.scs.carleton.ca/~soma/os-2019w/code/print.bin print.bin] which contains the machine code for a function that prints the given string to standard out.
# [2] Is a filesystem's superblock accessed frequently in normal operation?  Why or why not?
# [2] Is a filesystem's superblock accessed frequently in normal operation?  Why or why not?
# [2] When first connecting to a remote host via ssh, you will normally get a message saying something similar to this: "The authenticity of host 'access.scs.carleton.ca (134.117.29.72)' can't be established.  RSA key fingerprint is SHA256:MexEKZF0Os0Vl6VTObN70lRf2DFsGfD8DTQ7FKKqVJ4.  Are you sure you want to continue connecting (yes/no)?"
# [2] When first connecting to a remote host via ssh, you will normally get a message saying something similar to this: "The authenticity of host 'access.scs.carleton.ca (134.117.29.72)' can't be established.  RSA key fingerprint is SHA256:MexEKZF0Os0Vl6VTObN70lRf2DFsGfD8DTQ7FKKqVJ4.  Are you sure you want to continue connecting (yes/no)?"
#* Is it normally safe to say yes to this question?  Why?
#* Is it normally safe to say yes to this question?  Why?
#* Describe a situation when you should say no.  Give a specific example of a "bad thing" that could be happening.
#* Describe a situation when you should say no.  Give a specific example of a "bad thing" that could be happening.
# [3] Modify [https://homeostasis.scs.carleton.ca/~soma/os-2017f/code/tut3/3000test.c 3000test.c] so it can report on what access the current user has to the file.  Give your code as a "diff -c" relative to the original 3000test.c. (You may ignore groups.)
# [3] Create a program 3000run-write.c (based on 3000test.c) that attempts to run the contents of a (specific) file by mmap'ing it into an array, converting the array pointer into a function pointer, and then calling the function.  The called function should have the same declaration as write(), and you should call it with three arguments: 1, "Hello there!\n", and 13.  When your program is given the file [https://homeostasis.scs.carleton.ca/~soma/os-2019w/code/write.bin write.bin] as a command-line argument, it should print out "Hello there!" to standard out (the terminal).  Give the code of 3000run-write as a "diff -c" versus the original 3000test.c.
# [2] You run the commands below, replacing <scs-username> with your SCS username.  After running these commands (and assuming the sshfs command succeeds), how many copies of "test.txt" exist?  Where are they stored?
# [2] You run the commands below, replacing <scs-username> with your SCS username.  After running these commands (and assuming the sshfs command succeeds), how many copies of "test.txt" exist?  Where are they stored?


Line 34: Line 32:
     rm -f scs-files/testdir/test.txt
     rm -f scs-files/testdir/test.txt


'''EXTRA CREDIT (4 points):''' Make a file upper.bin that, when loaded by 3000run, prints out the upper case version of the string given as an argument.
'''EXTRA CREDIT (4 points):''' Make a file write-upper.bin that, when loaded by 3000run-write, writes out the upper case version of the string given as the second argument.  You may modify the passed in string.
 
If you submit the extra credit question, please upload your file write-upper.bin along with a text file containing the rest of your answers in a zip file (named like the text file, but with a .zip extension).  You should include the text of the assembly (or C code) of your program and explain how you used it to create write-upper.bin.
 
==Solutions==
 
===Question 1===
B is a hard link to A, so B and A refer to the same inode.  When you delete A, B is still referring to the same inode as A was, so if you could read A before, you'll be able to read B.
 
===Question 2===
 
B is a symbolic link to A, so B only refers to A by name.  When you delete A, B will refer to a file that no longer exists.  Thus attempts to read from B will fail, giving an error "No such file or directory".
 
===Question 3===
 
If fsck finds an allocated inode that no file refers to, it places it in lost+found (i.e., it creates a hard link for it in the lost+found directory, probably called #12345 or whatever the inode's number is).  This situation can happen if a directory gets corrupted or erased.
 
===Question 4===
 
If a backup program didn't specially treat sparse files, it could find itself reading and storing enormous files that had little to no data.  A 100G file could have only 10 bytes of information in it, for example.  Thus while the output medium would have plenty of space for 10 bytes, it might not have enough for 100Gs.
 
Having said this, in practice a backup program would primarily be slowed down if it didn't treat sparse files specially, as most backup programs compress their output.  They would still have to read all the bytes of the enormous files, but runs of zeros compress very well!
 
===Question 5===
 
A filesystem's superblock is rarely accessed during normal operation as it is only needed when a filesystem is mounted and unmounted.  Otherwise the information contained in the superblock is cached in RAM so the kernel can access other parts of the filesystem.
 
===Question 6===
 
* It is normally safe to say yes to this question because nobody is normally trying to actively interfere with ssh connections.
 
* If someone was trying to actively attack you, they could intercept and serve as an "intruder in the middle", observing and potentially modifying all information being sent in both directions via SSH.  The SSH protocol is designed to stop such attacks, but it is vulnerable on first connection because the attacker could substitute their key for the real host's key - and would then pretend to be the remote host and serving as a relay so it will appear the connection is direct, when in fact it is not.  In other words if you are being attacked when the question is asked and you say "yes", you can be compromised.
 
 
===Question 7===
 
<pre>
*** 3000test.c  2017-10-01 13:29:05.000000000 -0400
--- 3000test-access.c  2019-02-11 14:24:02.074844166 -0500
***************
*** 21,26 ****
--- 21,70 ----
          exit(-1);
  }
 
+ void output_access(struct stat *sb)
+ {
+        uid_t uid;
+        mode_t m = sb->st_mode;
+        int r = 0, w = 0, x = 0;
+
+       
+        uid = geteuid();
+
+        if (sb->st_uid == uid) {
+                if (S_IRUSR & m) {
+                        r = 1;
+                }
+                if (S_IWUSR & m) {
+                        w = 1;
+                }
+                if (S_IXUSR & m) {
+                        x = 1;
+                }
+        } else {
+                if (S_IROTH & m) {
+                        r = 1;
+                }
+                if (S_IWOTH & m) {
+                        w = 1;
+                }
+                if (S_IXOTH & m) {
+                        x = 1;
+                }
+        }
+
+        if (r) {
+                printf("File is readable by the current user.\n");
+        }
+
+        if (w) {
+                printf("File is writable by the current user.\n");
+        }
+
+        if (x) {
+                printf("File is executable by the current user.\n");
+        }
+ }
+
  int main(int argc, char *argv[])
  {
          struct stat statbuf;
***************
*** 51,56 ****
--- 95,102 ----
          printf("  inode %ld\n", statbuf.st_ino);
          printf("  length %ld\n", len);       
 
+        output_access(&statbuf);
+       
          if (S_ISREG(statbuf.st_mode)) {
                  fd = open(fn, O_RDONLY);
                  if (fd == -1) {
</pre>
 
 
===Question 8===
 
<pre>
*** 3000test.c  2017-10-01 13:29:05.000000000 -0400
--- 3000run-write.c    2019-02-01 00:24:15.138712481 -0500
***************
*** 26,33 ****
          struct stat statbuf;
          char *fn;
          int fd;
!        size_t len, i, count;
!       
          char *data;
 
          if (argc < 2) {
--- 26,35 ----
          struct stat statbuf;
          char *fn;
          int fd;
!        size_t len;
!
!        typedef ssize_t (*func_t)(int, char *, size_t);
!        func_t func;
          char *data;
 
          if (argc < 2) {
***************
*** 47,55 ****
          }
 
          len = statbuf.st_size;
-        printf("File %s: \n", fn);
-        printf("  inode %ld\n", statbuf.st_ino);
-        printf("  length %ld\n", len);       
 
          if (S_ISREG(statbuf.st_mode)) {
                  fd = open(fn, O_RDONLY);
--- 49,54 ----
***************
*** 57,76 ****
                          report_error(strerror(errno));
                  }
                  data = (char *) mmap(NULL, len,
!                                      PROT_READ, MAP_SHARED, fd, 0);
                  if (data == MAP_FAILED) {
                          report_error(strerror(errno));
                  }
 
!                count = 0;
!                for (i=0; i<len; i++) {
!                        if (data[i] == 'a') {
!                                count++;
!                        }
!                }
!
!                printf(" a count %ld\n", count);
!
                  if (munmap(data, len) == -1) {
                          report_error(strerror(errno));                       
                  }
--- 56,69 ----
                          report_error(strerror(errno));
                  }
                  data = (char *) mmap(NULL, len,
!                                      PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
                  if (data == MAP_FAILED) {
                          report_error(strerror(errno));
                  }
 
!                func = (func_t) data;
!                func(1, "Hello there!\n", 13);
!               
                  if (munmap(data, len) == -1) {
                          report_error(strerror(errno));                       
                  }
</pre>
 
===Question 9===
 
Assuming that you make the sshfs command succeed by adding the "-o nonempty" option, you'll have copies of test.txt in the following locations (~ = home directory):
* local: test.txt
* local: scs-files/test.txt
* remote: ~/testdir/test.txt
This happens because the rm commands fail:
* the first fails because the local scs-files/test.txt is masked by the mount
* the second fails because testdir was created on the remote system and the remote system was unmounted

Latest revision as of 21:04, 11 February 2019

Please submit the answers to the following questions via CULearn by 4 PM on Monday, February 11, 2019. There are 20 points in 9 questions (and 4 points extra credit).

Submit your answers as a single text file named "<username>-comp3000-assign2.txt" (where username is your MyCarletonOne username). The first four lines of this file should be "COMP 3000 Assignment 2", your name, student number, and the date of submission. You may wish to format your answers in Markdown to improve their appearance.

No other formats will be accepted. Submitting in another format will likely result in your assignment not being graded and you receiving no marks for this assignment. In particular do not submit an MS Word or OpenOffice file as your answers document!

Don't forget to include what outside resources you used to complete each of your answers, including other students, man pages, and web resources. You do not need to list help from the instructor, TA, or information found in the textbook.

Questions

  1. [2] Assume you have a file A. You type ln A B in order to create file B. If you delete A, what happens when you try to read B? Why?
  2. [2] Assume you have a file A. You type ln -s A B in order to create file B. If you delete A, what happens when you try to read B? Why?
  3. [2] What does fsck do if it finds an allocated inode that no file refers to? How could this problem arise?
  4. [2] Describe a situation where a UNIX sparse file could cause problems for a backup program (that didn't specially treat sparse files).
  5. [2] Is a filesystem's superblock accessed frequently in normal operation? Why or why not?
  6. [2] When first connecting to a remote host via ssh, you will normally get a message saying something similar to this: "The authenticity of host 'access.scs.carleton.ca (134.117.29.72)' can't be established. RSA key fingerprint is SHA256:MexEKZF0Os0Vl6VTObN70lRf2DFsGfD8DTQ7FKKqVJ4. Are you sure you want to continue connecting (yes/no)?"
    • Is it normally safe to say yes to this question? Why?
    • Describe a situation when you should say no. Give a specific example of a "bad thing" that could be happening.
  7. [3] Modify 3000test.c so it can report on what access the current user has to the file. Give your code as a "diff -c" relative to the original 3000test.c. (You may ignore groups.)
  8. [3] Create a program 3000run-write.c (based on 3000test.c) that attempts to run the contents of a (specific) file by mmap'ing it into an array, converting the array pointer into a function pointer, and then calling the function. The called function should have the same declaration as write(), and you should call it with three arguments: 1, "Hello there!\n", and 13. When your program is given the file write.bin as a command-line argument, it should print out "Hello there!" to standard out (the terminal). Give the code of 3000run-write as a "diff -c" versus the original 3000test.c.
  9. [2] You run the commands below, replacing <scs-username> with your SCS username. After running these commands (and assuming the sshfs command succeeds), how many copies of "test.txt" exist? Where are they stored?
    echo "This is random text" > test.txt
    mkdir scs-files
    cp test.txt scs-files
    sshfs <scs-username>@access.scs.carleton.ca:. scs-files
      <login to access.scs.carleton.ca>
    mkdir scs-files/testdir
    cp test.txt scs-files/testdir
    rm -f scs-files/test.txt
    fusermount -u scs-files
    rm -f scs-files/testdir/test.txt

EXTRA CREDIT (4 points): Make a file write-upper.bin that, when loaded by 3000run-write, writes out the upper case version of the string given as the second argument. You may modify the passed in string.

If you submit the extra credit question, please upload your file write-upper.bin along with a text file containing the rest of your answers in a zip file (named like the text file, but with a .zip extension). You should include the text of the assembly (or C code) of your program and explain how you used it to create write-upper.bin.

Solutions

Question 1

B is a hard link to A, so B and A refer to the same inode. When you delete A, B is still referring to the same inode as A was, so if you could read A before, you'll be able to read B.

Question 2

B is a symbolic link to A, so B only refers to A by name. When you delete A, B will refer to a file that no longer exists. Thus attempts to read from B will fail, giving an error "No such file or directory".

Question 3

If fsck finds an allocated inode that no file refers to, it places it in lost+found (i.e., it creates a hard link for it in the lost+found directory, probably called #12345 or whatever the inode's number is). This situation can happen if a directory gets corrupted or erased.

Question 4

If a backup program didn't specially treat sparse files, it could find itself reading and storing enormous files that had little to no data. A 100G file could have only 10 bytes of information in it, for example. Thus while the output medium would have plenty of space for 10 bytes, it might not have enough for 100Gs.

Having said this, in practice a backup program would primarily be slowed down if it didn't treat sparse files specially, as most backup programs compress their output. They would still have to read all the bytes of the enormous files, but runs of zeros compress very well!

Question 5

A filesystem's superblock is rarely accessed during normal operation as it is only needed when a filesystem is mounted and unmounted. Otherwise the information contained in the superblock is cached in RAM so the kernel can access other parts of the filesystem.

Question 6

  • It is normally safe to say yes to this question because nobody is normally trying to actively interfere with ssh connections.
  • If someone was trying to actively attack you, they could intercept and serve as an "intruder in the middle", observing and potentially modifying all information being sent in both directions via SSH. The SSH protocol is designed to stop such attacks, but it is vulnerable on first connection because the attacker could substitute their key for the real host's key - and would then pretend to be the remote host and serving as a relay so it will appear the connection is direct, when in fact it is not. In other words if you are being attacked when the question is asked and you say "yes", you can be compromised.


Question 7

*** 3000test.c  2017-10-01 13:29:05.000000000 -0400
--- 3000test-access.c   2019-02-11 14:24:02.074844166 -0500
***************
*** 21,26 ****
--- 21,70 ----
          exit(-1);
  }
  
+ void output_access(struct stat *sb)
+ {
+         uid_t uid;
+         mode_t m = sb->st_mode;
+         int r = 0, w = 0, x = 0;
+ 
+         
+         uid = geteuid();
+ 
+         if (sb->st_uid == uid) {
+                 if (S_IRUSR & m) {
+                         r = 1;
+                 }
+                 if (S_IWUSR & m) {
+                         w = 1;
+                 }
+                 if (S_IXUSR & m) {
+                         x = 1;
+                 }
+         } else {
+                 if (S_IROTH & m) {
+                         r = 1;
+                 }
+                 if (S_IWOTH & m) {
+                         w = 1;
+                 }
+                 if (S_IXOTH & m) {
+                         x = 1;
+                 }
+         }
+ 
+         if (r) {
+                 printf("File is readable by the current user.\n");
+         }
+ 
+         if (w) {
+                 printf("File is writable by the current user.\n");
+         }
+ 
+         if (x) {
+                 printf("File is executable by the current user.\n");
+         }
+ }
+ 
  int main(int argc, char *argv[])
  {
          struct stat statbuf;
***************
*** 51,56 ****
--- 95,102 ----
          printf("   inode %ld\n", statbuf.st_ino);
          printf("  length %ld\n", len);        
  
+         output_access(&statbuf);
+         
          if (S_ISREG(statbuf.st_mode)) {
                  fd = open(fn, O_RDONLY);
                  if (fd == -1) {


Question 8

*** 3000test.c  2017-10-01 13:29:05.000000000 -0400
--- 3000run-write.c     2019-02-01 00:24:15.138712481 -0500
***************
*** 26,33 ****
          struct stat statbuf;
          char *fn;
          int fd;
!         size_t len, i, count;
!         
          char *data;
  
          if (argc < 2) {
--- 26,35 ----
          struct stat statbuf;
          char *fn;
          int fd;
!         size_t len;
! 
!         typedef ssize_t (*func_t)(int, char *, size_t);
!         func_t func;
          char *data;
  
          if (argc < 2) {
***************
*** 47,55 ****
          }
  
          len = statbuf.st_size;
-         printf("File %s: \n", fn);
-         printf("   inode %ld\n", statbuf.st_ino);
-         printf("  length %ld\n", len);        
  
          if (S_ISREG(statbuf.st_mode)) {
                  fd = open(fn, O_RDONLY);
--- 49,54 ----
***************
*** 57,76 ****
                          report_error(strerror(errno));
                  }
                  data = (char *) mmap(NULL, len,
!                                      PROT_READ, MAP_SHARED, fd, 0);
                  if (data == MAP_FAILED) {
                          report_error(strerror(errno));
                  }
  
!                 count = 0;
!                 for (i=0; i<len; i++) {
!                         if (data[i] == 'a') {
!                                 count++;
!                         }
!                 }
! 
!                 printf(" a count %ld\n", count);
! 
                  if (munmap(data, len) == -1) {
                          report_error(strerror(errno));                        
                  }
--- 56,69 ----
                          report_error(strerror(errno));
                  }
                  data = (char *) mmap(NULL, len,
!                                      PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
                  if (data == MAP_FAILED) {
                          report_error(strerror(errno));
                  }
  
!                 func = (func_t) data;
!                 func(1, "Hello there!\n", 13);
!                 
                  if (munmap(data, len) == -1) {
                          report_error(strerror(errno));                        
                  }

Question 9

Assuming that you make the sshfs command succeed by adding the "-o nonempty" option, you'll have copies of test.txt in the following locations (~ = home directory):

  • local: test.txt
  • local: scs-files/test.txt
  • remote: ~/testdir/test.txt

This happens because the rm commands fail:

  • the first fails because the local scs-files/test.txt is masked by the mount
  • the second fails because testdir was created on the remote system and the remote system was unmounted