Integrate Samsung fsck_msdos fixes, including problem seeking beyond 4 gig

Change-Id: I8829a3a2c26625d7405fa0a43da1640ea5c9ffbc
Signed-off-by: Mike Lockwood <lockwood@google.com>
diff --git a/dir.c b/dir.c
index 5837463..b09ab53 100644
--- a/dir.c
+++ b/dir.c
@@ -321,8 +321,12 @@
 		}
 		off = startcl * boot->SecPerClust + boot->ClusterOffset;
 		off *= boot->BytesPerSec;
-		if (lseek(f, off, SEEK_SET) != off
-		    || read(f, delbuf, clsz) != clsz) {
+		if (lseek64(f, off, SEEK_SET) != off) {
+			printf("off = %llu\n", off);
+			perror("Unable to lseek64");
+			return FSFATAL;
+		}
+		if (read(f, delbuf, clsz) != clsz) {
 			perror("Unable to read directory");
 			return FSFATAL;
 		}
@@ -330,8 +334,12 @@
 			*s = SLOT_DELETED;
 			s += 32;
 		}
-		if (lseek(f, off, SEEK_SET) != off
-		    || write(f, delbuf, clsz) != clsz) {
+		if (lseek64(f, off, SEEK_SET) != off) {
+			printf("off = %llu\n", off);
+			perror("Unable to lseek64");
+			return FSFATAL;
+		}
+		if (write(f, delbuf, clsz) != clsz) {
 			perror("Unable to write directory");
 			return FSFATAL;
 		}
@@ -424,6 +432,87 @@
 	return FSOK;
 }
 
+
+static u_char  dot_header[16]={0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
+static u_char  dot_dot_header[16]={0x2E, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
+
+/*
+ * Check for missing or broken '.' and '..' entries.
+ */
+static int
+check_dot_dot(int f, struct bootblock *boot, struct fatEntry *fat,struct dosDirEntry *dir)
+{
+	u_char *p, *buf;
+	loff_t off;
+	int last;
+	cl_t cl;
+	int rc=0, n_count;
+
+	int dot, dotdot;
+	dot = dotdot = 0;
+	cl = dir->head;
+
+	if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
+		return rc;
+	}
+
+	do {
+		if (!(boot->flags & FAT32) && !dir->parent) {
+			last = boot->RootDirEnts * 32;
+			off = boot->ResSectors + boot->FATs * boot->FATsecs;
+		} else {
+			last = boot->SecPerClust * boot->BytesPerSec;
+			off = cl * boot->SecPerClust + boot->ClusterOffset;
+		}
+
+		off *= boot->BytesPerSec;
+		buf = malloc(last);
+		if (!buf) {
+			perror("Unable to malloc");
+			return FSFATAL;
+		}
+		if (lseek64(f, off, SEEK_SET) != off) {
+			printf("off = %llu\n", off);
+			perror("Unable to lseek64");
+			return FSFATAL;
+		}
+		if (read(f, buf, last) != last) {
+			perror("Unable to read");
+			return FSFATAL;
+		}
+		last /= 32;
+		p = buf;
+		for (n_count=0, rc=0; n_count < 11; n_count++) {
+			if (dot_header[n_count] != p[n_count]) {
+				rc=-1;
+				break;
+			}
+		}
+		 if(!rc)
+			dot=1;
+
+		for (n_count = 0, rc = 0; n_count < 11; n_count++) {
+			if (dot_dot_header[n_count] != p[n_count+32]) {
+				rc=-1;
+				break;
+			}
+		}
+		if(!rc)
+			dotdot=1;
+		free(buf);
+	} while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
+
+	if (!dot || !dotdot) {
+		if (!dot)
+			pwarn("%s: '.' absent for %s.\n",__func__,dir->name);
+
+		if (!dotdot)
+			pwarn("%s: '..' absent for %s. \n",__func__,dir->name);
+		return -1;
+	}
+	return 0;
+}
+
 /*
  * Read a directory and
  *   - resolve long name records
@@ -443,6 +532,8 @@
 	u_int lidx = 0;
 	int shortSum;
 	int mod = FSOK;
+	int n_count=0;
+	int rc=0;
 #define	THISMOD	0x8000			/* Only used within this routine */
 
 	cl = dir->head;
@@ -454,6 +545,9 @@
 	}
 	shortSum = -1;
 	vallfn = invlfn = empty = NULL;
+	int dot,dotdot;
+	dot = dotdot = 0;
+
 	do {
 		if (!(boot->flags & FAT32) && !dir->parent) {
 			last = boot->RootDirEnts * 32;
@@ -464,7 +558,7 @@
 		}
 
 		off *= boot->BytesPerSec;
-                if (lseek64(f, off, SEEK_SET) != off) {
+		if (lseek64(f, off, SEEK_SET) != off) {
                         printf("off = %llu\n", off);
 			perror("Unable to lseek64");
 			return FSFATAL;
@@ -474,9 +568,6 @@
 			return FSFATAL;
                 }
 		last /= 32;
-		/*
-		 * Check `.' and `..' entries here?			XXX
-		 */
 		for (p = buffer, i = 0; i < last; i++, p += 32) {
 			if (dir->fsckflags & DIREMPWARN) {
 				*p = SLOT_EMPTY;
@@ -758,11 +849,11 @@
 						mod |= FSERROR;
 				}
 				/*
-				 * handle `.' and `..' specially
+				 * handle '.' and '..' specially
 				 */
 				if (strcmp(dirent.name, ".") == 0) {
 					if (dirent.head != dir->head) {
-						pwarn("`.' entry in %s has incorrect start cluster\n",
+						pwarn("'.' entry in %s has incorrect start cluster\n",
 						      fullpath(dir));
 						if (ask(1, "Correct")) {
 							dirent.head = dir->head;
@@ -777,12 +868,11 @@
 							mod |= FSERROR;
 					}
 					continue;
-				}
-				if (strcmp(dirent.name, "..") == 0) {
+                } else if (strcmp(dirent.name, "..") == 0) {
 					if (dir->parent) {		/* XXX */
 						if (!dir->parent->parent) {
 							if (dirent.head) {
-								pwarn("`..' entry in %s has non-zero start cluster\n",
+								pwarn("'..' entry in %s has non-zero start cluster\n",
 								      fullpath(dir));
 								if (ask(1, "Correct")) {
 									dirent.head = 0;
@@ -794,7 +884,7 @@
 									mod |= FSERROR;
 							}
 						} else if (dirent.head != dir->parent->head) {
-							pwarn("`..' entry in %s has incorrect start cluster\n",
+							pwarn("'..' entry in %s has incorrect start cluster\n",
 							      fullpath(dir));
 							if (ask(1, "Correct")) {
 								dirent.head = dir->parent->head;
@@ -810,6 +900,31 @@
 						}
 					}
 					continue;
+				} else { //only one directory entry can point to dir->head, it's  '.'
+					if (dirent.head == dir->head) {
+						pwarn("%s entry in %s has incorrect start cluster.remove\n",
+								dirent.name, fullpath(dir));
+						//we have to remove this directory entry rigth now rigth here
+						if (ask(1, "Remove")) {
+							*p = SLOT_DELETED;
+							mod |= THISMOD|FSDIRMOD;
+						} else
+							mod |= FSERROR;
+						continue;
+					}
+					/* Consistency checking. a directory must have at least two entries:
+					   a dot (.) entry that points to itself, and a dot-dot (..)
+					   entry that points to its parent.
+					 */
+					if (check_dot_dot(f,boot,fat,&dirent)) {
+						//mark directory entry as deleted.
+						if (ask(1, "Remove")) {
+							*p = SLOT_DELETED;
+							mod |= THISMOD|FSDIRMOD;
+						} else
+							mod |= FSERROR;
+						continue;
+                    }
 				}
 
 				/* create directory tree node */
@@ -820,7 +935,10 @@
 				memcpy(d, &dirent, sizeof(struct dosDirEntry));
 				/* link it into the tree */
 				dir->child = d;
-
+#if 0
+				printf("%s: %s : 0x%02x:head %d, next 0x%0x parent 0x%0x child 0x%0x\n",
+						__func__,d->name,d->flags,d->head,d->next,d->parent,d->child);
+#endif
 				/* Enter this directory into the todo list */
 				if (!(n = newDirTodo())) {
 					perror("No space for todo list");
diff --git a/fat.c b/fat.c
index 8871407..2047c88 100644
--- a/fat.c
+++ b/fat.c
@@ -449,7 +449,7 @@
 int
 checkfat(struct bootblock *boot, struct fatEntry *fat)
 {
-	cl_t head, p, h, n;
+	cl_t head, p, h, n, wdk;
 	u_int len;
 	int ret = 0;
 	int conf;
@@ -466,8 +466,14 @@
 
 		/* follow the chain and mark all clusters on the way */
 		for (len = 0, p = head;
-		     p >= CLUST_FIRST && p < boot->NumClusters;
-		     p = fat[p].next) {
+			 p >= CLUST_FIRST && p < boot->NumClusters;
+			 p = fat[p].next) {
+				/* we have to check the len, to avoid infinite loop */
+				if (len > boot->NumClusters) {
+					printf("detect cluster chain loop: head %u for p %u\n", head, p);
+					break;
+			}
+
 			fat[p].head = head;
 			len++;
 		}
@@ -487,11 +493,14 @@
 			continue;
 
 		/* follow the chain to its end (hopefully) */
-		for (p = head;
-		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
-		     p = n)
+		/* also possible infinite loop, that's why I insert wdk counter */
+		for (p = head,wdk=boot->NumClusters;
+		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters && wdk;
+				 p = n,wdk--) {
 			if (fat[n].head != head)
 				break;
+		}
+
 		if (n >= CLUST_EOFS)
 			continue;
 
@@ -692,9 +701,16 @@
 				ret = 1;
 			}
 		}
+
+		if (boot->FSNext > boot->NumClusters  ) {
+			pwarn("FSNext block (%d) not correct NumClusters (%d)\n",
+					boot->FSNext, boot->NumClusters);
+			boot->FSNext=CLUST_FIRST; // boot->FSNext can have -1 value.
+		}
+
 		if (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE) {
 			pwarn("Next free cluster in FSInfo block (%u) not free\n",
-			      boot->FSNext);
+					boot->FSNext);
 			if (ask(1, "Fix"))
 				for (head = CLUST_FIRST; head < boot->NumClusters; head++)
 					if (fat[head].next == CLUST_FREE) {