diff options
author | Anton Altaparmakov <aia21@cantab.net> | 2005-06-25 11:31:27 -0400 |
---|---|---|
committer | Anton Altaparmakov <aia21@cantab.net> | 2005-06-25 11:31:27 -0400 |
commit | ca8fd7a0c6aa835e8014830b290cb965e85ac88e (patch) | |
tree | 504929d2a4beacb86fbc420c85f5c102f2a27fed | |
parent | 9f993fe4634b39ca4404ba278053b03f360ec08a (diff) |
NTFS: Detect the case when Windows has been suspended to disk on the volume
to be mounted and if this is the case do not allow (re)mounting
read-write. This is done by parsing hiberfil.sys if present.
Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
-rw-r--r-- | fs/ntfs/ChangeLog | 3 | ||||
-rw-r--r-- | fs/ntfs/super.c | 179 |
2 files changed, 171 insertions, 11 deletions
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog index 59ecca4297bb..c089bf0c02ac 100644 --- a/fs/ntfs/ChangeLog +++ b/fs/ntfs/ChangeLog | |||
@@ -125,6 +125,9 @@ ToDo/Notes: | |||
125 | if the runlist was not mapped at all and a mapping error occured we | 125 | if the runlist was not mapped at all and a mapping error occured we |
126 | would leave the runlist locked on exit to the function so that the | 126 | would leave the runlist locked on exit to the function so that the |
127 | next access to the same file would try to take the lock and deadlock. | 127 | next access to the same file would try to take the lock and deadlock. |
128 | - Detect the case when Windows has been suspended to disk on the volume | ||
129 | to be mounted and if this is the case do not allow (re)mounting | ||
130 | read-write. This is done by parsing hiberfil.sys if present. | ||
128 | 131 | ||
129 | 2.1.22 - Many bug and race fixes and error handling improvements. | 132 | 2.1.22 - Many bug and race fixes and error handling improvements. |
130 | 133 | ||
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 92e1d28219b3..41aa8eb6755b 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c | |||
@@ -1156,6 +1156,124 @@ static BOOL load_and_check_logfile(ntfs_volume *vol) | |||
1156 | return TRUE; | 1156 | return TRUE; |
1157 | } | 1157 | } |
1158 | 1158 | ||
1159 | #define NTFS_HIBERFIL_HEADER_SIZE 4096 | ||
1160 | |||
1161 | /** | ||
1162 | * check_windows_hibernation_status - check if Windows is suspended on a volume | ||
1163 | * @vol: ntfs super block of device to check | ||
1164 | * | ||
1165 | * Check if Windows is hibernated on the ntfs volume @vol. This is done by | ||
1166 | * looking for the file hiberfil.sys in the root directory of the volume. If | ||
1167 | * the file is not present Windows is definitely not suspended. | ||
1168 | * | ||
1169 | * If hiberfil.sys exists and is less than 4kiB in size it means Windows is | ||
1170 | * definitely suspended (this volume is not the system volume). Caveat: on a | ||
1171 | * system with many volumes it is possible that the < 4kiB check is bogus but | ||
1172 | * for now this should do fine. | ||
1173 | * | ||
1174 | * If hiberfil.sys exists and is larger than 4kiB in size, we need to read the | ||
1175 | * hiberfil header (which is the first 4kiB). If this begins with "hibr", | ||
1176 | * Windows is definitely suspended. If it is completely full of zeroes, | ||
1177 | * Windows is definitely not hibernated. Any other case is treated as if | ||
1178 | * Windows is suspended. This caters for the above mentioned caveat of a | ||
1179 | * system with many volumes where no "hibr" magic would be present and there is | ||
1180 | * no zero header. | ||
1181 | * | ||
1182 | * Return 0 if Windows is not hibernated on the volume, >0 if Windows is | ||
1183 | * hibernated on the volume, and -errno on error. | ||
1184 | */ | ||
1185 | static int check_windows_hibernation_status(ntfs_volume *vol) | ||
1186 | { | ||
1187 | MFT_REF mref; | ||
1188 | struct inode *vi; | ||
1189 | ntfs_inode *ni; | ||
1190 | struct page *page; | ||
1191 | u32 *kaddr, *kend; | ||
1192 | ntfs_name *name = NULL; | ||
1193 | int ret = 1; | ||
1194 | static const ntfschar hiberfil[13] = { const_cpu_to_le16('h'), | ||
1195 | const_cpu_to_le16('i'), const_cpu_to_le16('b'), | ||
1196 | const_cpu_to_le16('e'), const_cpu_to_le16('r'), | ||
1197 | const_cpu_to_le16('f'), const_cpu_to_le16('i'), | ||
1198 | const_cpu_to_le16('l'), const_cpu_to_le16('.'), | ||
1199 | const_cpu_to_le16('s'), const_cpu_to_le16('y'), | ||
1200 | const_cpu_to_le16('s'), 0 }; | ||
1201 | |||
1202 | ntfs_debug("Entering."); | ||
1203 | /* | ||
1204 | * Find the inode number for the hibernation file by looking up the | ||
1205 | * filename hiberfil.sys in the root directory. | ||
1206 | */ | ||
1207 | down(&vol->root_ino->i_sem); | ||
1208 | mref = ntfs_lookup_inode_by_name(NTFS_I(vol->root_ino), hiberfil, 12, | ||
1209 | &name); | ||
1210 | up(&vol->root_ino->i_sem); | ||
1211 | if (IS_ERR_MREF(mref)) { | ||
1212 | ret = MREF_ERR(mref); | ||
1213 | /* If the file does not exist, Windows is not hibernated. */ | ||
1214 | if (ret == -ENOENT) { | ||
1215 | ntfs_debug("hiberfil.sys not present. Windows is not " | ||
1216 | "hibernated on the volume."); | ||
1217 | return 0; | ||
1218 | } | ||
1219 | /* A real error occured. */ | ||
1220 | ntfs_error(vol->sb, "Failed to find inode number for " | ||
1221 | "hiberfil.sys."); | ||
1222 | return ret; | ||
1223 | } | ||
1224 | /* We do not care for the type of match that was found. */ | ||
1225 | kfree(name); | ||
1226 | /* Get the inode. */ | ||
1227 | vi = ntfs_iget(vol->sb, MREF(mref)); | ||
1228 | if (IS_ERR(vi) || is_bad_inode(vi)) { | ||
1229 | if (!IS_ERR(vi)) | ||
1230 | iput(vi); | ||
1231 | ntfs_error(vol->sb, "Failed to load hiberfil.sys."); | ||
1232 | return IS_ERR(vi) ? PTR_ERR(vi) : -EIO; | ||
1233 | } | ||
1234 | if (unlikely(i_size_read(vi) < NTFS_HIBERFIL_HEADER_SIZE)) { | ||
1235 | ntfs_debug("hiberfil.sys is smaller than 4kiB (0x%llx). " | ||
1236 | "Windows is hibernated on the volume. This " | ||
1237 | "is not the system volume.", i_size_read(vi)); | ||
1238 | goto iput_out; | ||
1239 | } | ||
1240 | ni = NTFS_I(vi); | ||
1241 | page = ntfs_map_page(vi->i_mapping, 0); | ||
1242 | if (IS_ERR(page)) { | ||
1243 | ntfs_error(vol->sb, "Failed to read from hiberfil.sys."); | ||
1244 | ret = PTR_ERR(page); | ||
1245 | goto iput_out; | ||
1246 | } | ||
1247 | kaddr = (u32*)page_address(page); | ||
1248 | if (*(le32*)kaddr == const_cpu_to_le32(0x72626968)/*'hibr'*/) { | ||
1249 | ntfs_debug("Magic \"hibr\" found in hiberfil.sys. Windows is " | ||
1250 | "hibernated on the volume. This is the " | ||
1251 | "system volume."); | ||
1252 | goto unm_iput_out; | ||
1253 | } | ||
1254 | kend = kaddr + NTFS_HIBERFIL_HEADER_SIZE/sizeof(*kaddr); | ||
1255 | do { | ||
1256 | if (unlikely(*kaddr)) { | ||
1257 | ntfs_debug("hiberfil.sys is larger than 4kiB " | ||
1258 | "(0x%llx), does not contain the " | ||
1259 | "\"hibr\" magic, and does not have a " | ||
1260 | "zero header. Windows is hibernated " | ||
1261 | "on the volume. This is not the " | ||
1262 | "system volume.", i_size_read(vi)); | ||
1263 | goto unm_iput_out; | ||
1264 | } | ||
1265 | } while (++kaddr < kend); | ||
1266 | ntfs_debug("hiberfil.sys contains a zero header. Windows is not " | ||
1267 | "hibernated on the volume. This is the system " | ||
1268 | "volume."); | ||
1269 | ret = 0; | ||
1270 | unm_iput_out: | ||
1271 | ntfs_unmap_page(page); | ||
1272 | iput_out: | ||
1273 | iput(vi); | ||
1274 | return ret; | ||
1275 | } | ||
1276 | |||
1159 | /** | 1277 | /** |
1160 | * load_and_init_quota - load and setup the quota file for a volume if present | 1278 | * load_and_init_quota - load and setup the quota file for a volume if present |
1161 | * @vol: ntfs super block describing device whose quota file to load | 1279 | * @vol: ntfs super block describing device whose quota file to load |
@@ -1570,6 +1688,9 @@ static BOOL load_system_files(ntfs_volume *vol) | |||
1570 | MFT_RECORD *m; | 1688 | MFT_RECORD *m; |
1571 | VOLUME_INFORMATION *vi; | 1689 | VOLUME_INFORMATION *vi; |
1572 | ntfs_attr_search_ctx *ctx; | 1690 | ntfs_attr_search_ctx *ctx; |
1691 | #ifdef NTFS_RW | ||
1692 | int err; | ||
1693 | #endif /* NTFS_RW */ | ||
1573 | 1694 | ||
1574 | ntfs_debug("Entering."); | 1695 | ntfs_debug("Entering."); |
1575 | #ifdef NTFS_RW | 1696 | #ifdef NTFS_RW |
@@ -1746,6 +1867,50 @@ get_ctx_vol_failed: | |||
1746 | /* This will prevent a read-write remount. */ | 1867 | /* This will prevent a read-write remount. */ |
1747 | NVolSetErrors(vol); | 1868 | NVolSetErrors(vol); |
1748 | } | 1869 | } |
1870 | #endif /* NTFS_RW */ | ||
1871 | /* Get the root directory inode so we can do path lookups. */ | ||
1872 | vol->root_ino = ntfs_iget(sb, FILE_root); | ||
1873 | if (IS_ERR(vol->root_ino) || is_bad_inode(vol->root_ino)) { | ||
1874 | if (!IS_ERR(vol->root_ino)) | ||
1875 | iput(vol->root_ino); | ||
1876 | ntfs_error(sb, "Failed to load root directory."); | ||
1877 | goto iput_logfile_err_out; | ||
1878 | } | ||
1879 | #ifdef NTFS_RW | ||
1880 | /* | ||
1881 | * Check if Windows is suspended to disk on the target volume. If it | ||
1882 | * is hibernated, we must not write *anything* to the disk so set | ||
1883 | * NVolErrors() without setting the dirty volume flag and mount | ||
1884 | * read-only. This will prevent read-write remounting and it will also | ||
1885 | * prevent all writes. | ||
1886 | */ | ||
1887 | err = check_windows_hibernation_status(vol); | ||
1888 | if (unlikely(err)) { | ||
1889 | static const char *es1a = "Failed to determine if Windows is " | ||
1890 | "hibernated"; | ||
1891 | static const char *es1b = "Windows is hibernated"; | ||
1892 | static const char *es2 = ". Run chkdsk."; | ||
1893 | const char *es1; | ||
1894 | |||
1895 | es1 = err < 0 ? es1a : es1b; | ||
1896 | /* If a read-write mount, convert it to a read-only mount. */ | ||
1897 | if (!(sb->s_flags & MS_RDONLY)) { | ||
1898 | if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | | ||
1899 | ON_ERRORS_CONTINUE))) { | ||
1900 | ntfs_error(sb, "%s and neither on_errors=" | ||
1901 | "continue nor on_errors=" | ||
1902 | "remount-ro was specified%s", | ||
1903 | es1, es2); | ||
1904 | goto iput_root_err_out; | ||
1905 | } | ||
1906 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; | ||
1907 | ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); | ||
1908 | } else | ||
1909 | ntfs_warning(sb, "%s. Will not be able to remount " | ||
1910 | "read-write%s", es1, es2); | ||
1911 | /* This will prevent a read-write remount. */ | ||
1912 | NVolSetErrors(vol); | ||
1913 | } | ||
1749 | /* If (still) a read-write mount, mark the volume dirty. */ | 1914 | /* If (still) a read-write mount, mark the volume dirty. */ |
1750 | if (!(sb->s_flags & MS_RDONLY) && | 1915 | if (!(sb->s_flags & MS_RDONLY) && |
1751 | ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) { | 1916 | ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) { |
@@ -1759,7 +1924,7 @@ get_ctx_vol_failed: | |||
1759 | ntfs_error(sb, "%s and neither on_errors=continue nor " | 1924 | ntfs_error(sb, "%s and neither on_errors=continue nor " |
1760 | "on_errors=remount-ro was specified%s", | 1925 | "on_errors=remount-ro was specified%s", |
1761 | es1, es2); | 1926 | es1, es2); |
1762 | goto iput_logfile_err_out; | 1927 | goto iput_root_err_out; |
1763 | } | 1928 | } |
1764 | ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); | 1929 | ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); |
1765 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; | 1930 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; |
@@ -1786,7 +1951,7 @@ get_ctx_vol_failed: | |||
1786 | ntfs_error(sb, "%s and neither on_errors=continue nor " | 1951 | ntfs_error(sb, "%s and neither on_errors=continue nor " |
1787 | "on_errors=remount-ro was specified%s", | 1952 | "on_errors=remount-ro was specified%s", |
1788 | es1, es2); | 1953 | es1, es2); |
1789 | goto iput_logfile_err_out; | 1954 | goto iput_root_err_out; |
1790 | } | 1955 | } |
1791 | ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); | 1956 | ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); |
1792 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; | 1957 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; |
@@ -1805,21 +1970,13 @@ get_ctx_vol_failed: | |||
1805 | ntfs_error(sb, "%s and neither on_errors=continue nor " | 1970 | ntfs_error(sb, "%s and neither on_errors=continue nor " |
1806 | "on_errors=remount-ro was specified%s", | 1971 | "on_errors=remount-ro was specified%s", |
1807 | es1, es2); | 1972 | es1, es2); |
1808 | goto iput_logfile_err_out; | 1973 | goto iput_root_err_out; |
1809 | } | 1974 | } |
1810 | ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); | 1975 | ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); |
1811 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; | 1976 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; |
1812 | NVolSetErrors(vol); | 1977 | NVolSetErrors(vol); |
1813 | } | 1978 | } |
1814 | #endif /* NTFS_RW */ | 1979 | #endif /* NTFS_RW */ |
1815 | /* Get the root directory inode. */ | ||
1816 | vol->root_ino = ntfs_iget(sb, FILE_root); | ||
1817 | if (IS_ERR(vol->root_ino) || is_bad_inode(vol->root_ino)) { | ||
1818 | if (!IS_ERR(vol->root_ino)) | ||
1819 | iput(vol->root_ino); | ||
1820 | ntfs_error(sb, "Failed to load root directory."); | ||
1821 | goto iput_logfile_err_out; | ||
1822 | } | ||
1823 | /* If on NTFS versions before 3.0, we are done. */ | 1980 | /* If on NTFS versions before 3.0, we are done. */ |
1824 | if (unlikely(vol->major_ver < 3)) | 1981 | if (unlikely(vol->major_ver < 3)) |
1825 | return TRUE; | 1982 | return TRUE; |