diff --git a/Makefile.build b/Makefile.build
index 39f59e1799b51fb8c9e84037d0c9203f468734e2..06c26277e141eb0fe57b90c142ff71a6405e16b0 100644
--- a/Makefile.build
+++ b/Makefile.build
@@ -73,6 +73,7 @@ userprog_SRC += userprog/syscall_write.c
 userprog_SRC += userprog/file_descriptors_map.c
 userprog_SRC += userprog/syscall_read.c
 userprog_SRC += userprog/syscall_filesize.c
+userprog_SRC += userprog/syscall_tell.c
 
 # No virtual memory code yet.
 #vm_SRC = vm/file.c			# Some file.
diff --git a/userprog/syscall_tell.c b/userprog/syscall_tell.c
new file mode 100644
index 0000000000000000000000000000000000000000..4fa049a1344557df1bbe67642562d89a11f103e3
--- /dev/null
+++ b/userprog/syscall_tell.c
@@ -0,0 +1,19 @@
+/*
+ * Returns the current offset into the file that the given file descriptor is at
+ *
+ * Authored by Joshua Saxby
+ */
+#include <stddef.h>
+#include "system_calls.h"
+#include "filesys/file.h"
+
+void syscall_tell(struct intr_frame *f) {
+	// pop off first int argument from interrupt frame
+	int file_descriptor = *((int*)f->esp + 1);
+	struct file *file = get_associated_file_pointer(file_descriptor);
+    /*
+     * tell() return type is unsigned so we can't return a special error code if
+     * file descriptor passed is invalid. Instead, we return 0 silently.
+     */
+	f->eax = (file != NULL) ? file_tell(file) : 0;
+}
diff --git a/userprog/system_calls.h b/userprog/system_calls.h
index b22867040cc55488aa61fdffd7dca93e74638288..ebeed4c37da16c0d0dfdec3ce5df30f25719bc5e 100644
--- a/userprog/system_calls.h
+++ b/userprog/system_calls.h
@@ -69,6 +69,12 @@ void syscall_filesize(struct intr_frame *f);
  */
 void syscall_remove(struct intr_frame *f);
 
+/*
+ * Returns the position of the next byte to be read or written in open file fd,
+ * expressed in bytes from the beginning of the file. 
+ */
+void syscall_tell(struct intr_frame *f);
+
 /*
  * special additional stuff for handling file descriptors because they're annoying
  */