diff options
author | James Morris <jmorris@namei.org> | 2008-11-18 02:52:37 -0500 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2008-11-18 02:52:37 -0500 |
commit | f3a5c547012a09f38f7c27b17a8e3150b69cd259 (patch) | |
tree | 4d1d47382a4a445fc7ef7431bcf5d06b7cca8539 /fs | |
parent | e50a906e0200084f04f8f3b7c3a14b0442d1347f (diff) | |
parent | 4e14e833ac3b97a4aa8803eea49f899adc5bb5f4 (diff) |
Merge branch 'master' into next
Conflicts:
fs/cifs/misc.c
Merge to resolve above, per the patch below.
Signed-off-by: James Morris <jmorris@namei.org>
diff --cc fs/cifs/misc.c
index ec36410,addd1dc..0000000
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@@ -347,13 -338,13 +338,13 @@@ header_assemble(struct smb_hdr *buffer
/* BB Add support for establishing new tCon and SMB Session */
/* with userid/password pairs found on the smb session */
/* for other target tcp/ip addresses BB */
- if (current->fsuid != treeCon->ses->linux_uid) {
+ if (current_fsuid() != treeCon->ses->linux_uid) {
cFYI(1, ("Multiuser mode and UID "
"did not match tcon uid"));
- read_lock(&GlobalSMBSeslock);
- list_for_each(temp_item, &GlobalSMBSessionList) {
- ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
+ ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
- if (ses->linux_uid == current->fsuid) {
+ if (ses->linux_uid == current_fsuid()) {
if (ses->server == treeCon->ses->server) {
cFYI(1, ("found matching uid substitute right smb_uid"));
buffer->Uid = ses->Suid;
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifs_debug.c | 277 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 71 | ||||
-rw-r--r-- | fs/cifs/cifs_spnego.c | 4 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 30 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 41 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 136 | ||||
-rw-r--r-- | fs/cifs/connect.c | 825 | ||||
-rw-r--r-- | fs/cifs/file.c | 7 | ||||
-rw-r--r-- | fs/cifs/misc.c | 90 | ||||
-rw-r--r-- | fs/inotify.c | 150 | ||||
-rw-r--r-- | fs/proc/proc_sysctl.c | 1 |
11 files changed, 910 insertions, 722 deletions
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 69a12aae91d3..490e34bbf27a 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c | |||
@@ -107,12 +107,13 @@ void cifs_dump_mids(struct TCP_Server_Info *server) | |||
107 | #ifdef CONFIG_PROC_FS | 107 | #ifdef CONFIG_PROC_FS |
108 | static int cifs_debug_data_proc_show(struct seq_file *m, void *v) | 108 | static int cifs_debug_data_proc_show(struct seq_file *m, void *v) |
109 | { | 109 | { |
110 | struct list_head *tmp; | 110 | struct list_head *tmp1, *tmp2, *tmp3; |
111 | struct list_head *tmp1; | ||
112 | struct mid_q_entry *mid_entry; | 111 | struct mid_q_entry *mid_entry; |
112 | struct TCP_Server_Info *server; | ||
113 | struct cifsSesInfo *ses; | 113 | struct cifsSesInfo *ses; |
114 | struct cifsTconInfo *tcon; | 114 | struct cifsTconInfo *tcon; |
115 | int i; | 115 | int i, j; |
116 | __u32 dev_type; | ||
116 | 117 | ||
117 | seq_puts(m, | 118 | seq_puts(m, |
118 | "Display Internal CIFS Data Structures for Debugging\n" | 119 | "Display Internal CIFS Data Structures for Debugging\n" |
@@ -122,46 +123,78 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) | |||
122 | seq_printf(m, "Servers:"); | 123 | seq_printf(m, "Servers:"); |
123 | 124 | ||
124 | i = 0; | 125 | i = 0; |
125 | read_lock(&GlobalSMBSeslock); | 126 | read_lock(&cifs_tcp_ses_lock); |
126 | list_for_each(tmp, &GlobalSMBSessionList) { | 127 | list_for_each(tmp1, &cifs_tcp_ses_list) { |
128 | server = list_entry(tmp1, struct TCP_Server_Info, | ||
129 | tcp_ses_list); | ||
127 | i++; | 130 | i++; |
128 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); | 131 | list_for_each(tmp2, &server->smb_ses_list) { |
129 | if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || | 132 | ses = list_entry(tmp2, struct cifsSesInfo, |
130 | (ses->serverNOS == NULL)) { | 133 | smb_ses_list); |
131 | seq_printf(m, "\nentry for %s not fully " | 134 | if ((ses->serverDomain == NULL) || |
132 | "displayed\n\t", ses->serverName); | 135 | (ses->serverOS == NULL) || |
133 | } else { | 136 | (ses->serverNOS == NULL)) { |
134 | seq_printf(m, | 137 | seq_printf(m, "\n%d) entry for %s not fully " |
135 | "\n%d) Name: %s Domain: %s Mounts: %d OS:" | 138 | "displayed\n\t", i, ses->serverName); |
136 | " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB" | 139 | } else { |
140 | seq_printf(m, | ||
141 | "\n%d) Name: %s Domain: %s Uses: %d OS:" | ||
142 | " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB" | ||
137 | " session status: %d\t", | 143 | " session status: %d\t", |
138 | i, ses->serverName, ses->serverDomain, | 144 | i, ses->serverName, ses->serverDomain, |
139 | atomic_read(&ses->inUse), | 145 | ses->ses_count, ses->serverOS, ses->serverNOS, |
140 | ses->serverOS, ses->serverNOS, | ||
141 | ses->capabilities, ses->status); | 146 | ses->capabilities, ses->status); |
142 | } | 147 | } |
143 | if (ses->server) { | ||
144 | seq_printf(m, "TCP status: %d\n\tLocal Users To " | 148 | seq_printf(m, "TCP status: %d\n\tLocal Users To " |
145 | "Server: %d SecMode: 0x%x Req On Wire: %d", | 149 | "Server: %d SecMode: 0x%x Req On Wire: %d", |
146 | ses->server->tcpStatus, | 150 | server->tcpStatus, server->srv_count, |
147 | atomic_read(&ses->server->socketUseCount), | 151 | server->secMode, |
148 | ses->server->secMode, | 152 | atomic_read(&server->inFlight)); |
149 | atomic_read(&ses->server->inFlight)); | ||
150 | 153 | ||
151 | #ifdef CONFIG_CIFS_STATS2 | 154 | #ifdef CONFIG_CIFS_STATS2 |
152 | seq_printf(m, " In Send: %d In MaxReq Wait: %d", | 155 | seq_printf(m, " In Send: %d In MaxReq Wait: %d", |
153 | atomic_read(&ses->server->inSend), | 156 | atomic_read(&server->inSend), |
154 | atomic_read(&ses->server->num_waiters)); | 157 | atomic_read(&server->num_waiters)); |
155 | #endif | 158 | #endif |
156 | 159 | ||
157 | seq_puts(m, "\nMIDs:\n"); | 160 | seq_puts(m, "\n\tShares:"); |
161 | j = 0; | ||
162 | list_for_each(tmp3, &ses->tcon_list) { | ||
163 | tcon = list_entry(tmp3, struct cifsTconInfo, | ||
164 | tcon_list); | ||
165 | ++j; | ||
166 | dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); | ||
167 | seq_printf(m, "\n\t%d) %s Mounts: %d ", j, | ||
168 | tcon->treeName, tcon->tc_count); | ||
169 | if (tcon->nativeFileSystem) { | ||
170 | seq_printf(m, "Type: %s ", | ||
171 | tcon->nativeFileSystem); | ||
172 | } | ||
173 | seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x" | ||
174 | "\nPathComponentMax: %d Status: 0x%d", | ||
175 | le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), | ||
176 | le32_to_cpu(tcon->fsAttrInfo.Attributes), | ||
177 | le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), | ||
178 | tcon->tidStatus); | ||
179 | if (dev_type == FILE_DEVICE_DISK) | ||
180 | seq_puts(m, " type: DISK "); | ||
181 | else if (dev_type == FILE_DEVICE_CD_ROM) | ||
182 | seq_puts(m, " type: CDROM "); | ||
183 | else | ||
184 | seq_printf(m, " type: %d ", dev_type); | ||
185 | |||
186 | if (tcon->need_reconnect) | ||
187 | seq_puts(m, "\tDISCONNECTED "); | ||
188 | seq_putc(m, '\n'); | ||
189 | } | ||
190 | |||
191 | seq_puts(m, "\n\tMIDs:\n"); | ||
158 | 192 | ||
159 | spin_lock(&GlobalMid_Lock); | 193 | spin_lock(&GlobalMid_Lock); |
160 | list_for_each(tmp1, &ses->server->pending_mid_q) { | 194 | list_for_each(tmp3, &server->pending_mid_q) { |
161 | mid_entry = list_entry(tmp1, struct | 195 | mid_entry = list_entry(tmp3, struct mid_q_entry, |
162 | mid_q_entry, | ||
163 | qhead); | 196 | qhead); |
164 | seq_printf(m, "State: %d com: %d pid:" | 197 | seq_printf(m, "\tState: %d com: %d pid:" |
165 | " %d tsk: %p mid %d\n", | 198 | " %d tsk: %p mid %d\n", |
166 | mid_entry->midState, | 199 | mid_entry->midState, |
167 | (int)mid_entry->command, | 200 | (int)mid_entry->command, |
@@ -171,44 +204,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) | |||
171 | } | 204 | } |
172 | spin_unlock(&GlobalMid_Lock); | 205 | spin_unlock(&GlobalMid_Lock); |
173 | } | 206 | } |
174 | |||
175 | } | ||
176 | read_unlock(&GlobalSMBSeslock); | ||
177 | seq_putc(m, '\n'); | ||
178 | |||
179 | seq_puts(m, "Shares:"); | ||
180 | |||
181 | i = 0; | ||
182 | read_lock(&GlobalSMBSeslock); | ||
183 | list_for_each(tmp, &GlobalTreeConnectionList) { | ||
184 | __u32 dev_type; | ||
185 | i++; | ||
186 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | ||
187 | dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); | ||
188 | seq_printf(m, "\n%d) %s Uses: %d ", i, | ||
189 | tcon->treeName, atomic_read(&tcon->useCount)); | ||
190 | if (tcon->nativeFileSystem) { | ||
191 | seq_printf(m, "Type: %s ", | ||
192 | tcon->nativeFileSystem); | ||
193 | } | ||
194 | seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x" | ||
195 | "\nPathComponentMax: %d Status: %d", | ||
196 | le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), | ||
197 | le32_to_cpu(tcon->fsAttrInfo.Attributes), | ||
198 | le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), | ||
199 | tcon->tidStatus); | ||
200 | if (dev_type == FILE_DEVICE_DISK) | ||
201 | seq_puts(m, " type: DISK "); | ||
202 | else if (dev_type == FILE_DEVICE_CD_ROM) | ||
203 | seq_puts(m, " type: CDROM "); | ||
204 | else | ||
205 | seq_printf(m, " type: %d ", dev_type); | ||
206 | |||
207 | if (tcon->tidStatus == CifsNeedReconnect) | ||
208 | seq_puts(m, "\tDISCONNECTED "); | ||
209 | } | 207 | } |
210 | read_unlock(&GlobalSMBSeslock); | 208 | read_unlock(&cifs_tcp_ses_lock); |
211 | |||
212 | seq_putc(m, '\n'); | 209 | seq_putc(m, '\n'); |
213 | 210 | ||
214 | /* BB add code to dump additional info such as TCP session info now */ | 211 | /* BB add code to dump additional info such as TCP session info now */ |
@@ -234,7 +231,9 @@ static ssize_t cifs_stats_proc_write(struct file *file, | |||
234 | { | 231 | { |
235 | char c; | 232 | char c; |
236 | int rc; | 233 | int rc; |
237 | struct list_head *tmp; | 234 | struct list_head *tmp1, *tmp2, *tmp3; |
235 | struct TCP_Server_Info *server; | ||
236 | struct cifsSesInfo *ses; | ||
238 | struct cifsTconInfo *tcon; | 237 | struct cifsTconInfo *tcon; |
239 | 238 | ||
240 | rc = get_user(c, buffer); | 239 | rc = get_user(c, buffer); |
@@ -242,33 +241,42 @@ static ssize_t cifs_stats_proc_write(struct file *file, | |||
242 | return rc; | 241 | return rc; |
243 | 242 | ||
244 | if (c == '1' || c == 'y' || c == 'Y' || c == '0') { | 243 | if (c == '1' || c == 'y' || c == 'Y' || c == '0') { |
245 | read_lock(&GlobalSMBSeslock); | ||
246 | #ifdef CONFIG_CIFS_STATS2 | 244 | #ifdef CONFIG_CIFS_STATS2 |
247 | atomic_set(&totBufAllocCount, 0); | 245 | atomic_set(&totBufAllocCount, 0); |
248 | atomic_set(&totSmBufAllocCount, 0); | 246 | atomic_set(&totSmBufAllocCount, 0); |
249 | #endif /* CONFIG_CIFS_STATS2 */ | 247 | #endif /* CONFIG_CIFS_STATS2 */ |
250 | list_for_each(tmp, &GlobalTreeConnectionList) { | 248 | read_lock(&cifs_tcp_ses_lock); |
251 | tcon = list_entry(tmp, struct cifsTconInfo, | 249 | list_for_each(tmp1, &cifs_tcp_ses_list) { |
252 | cifsConnectionList); | 250 | server = list_entry(tmp1, struct TCP_Server_Info, |
253 | atomic_set(&tcon->num_smbs_sent, 0); | 251 | tcp_ses_list); |
254 | atomic_set(&tcon->num_writes, 0); | 252 | list_for_each(tmp2, &server->smb_ses_list) { |
255 | atomic_set(&tcon->num_reads, 0); | 253 | ses = list_entry(tmp2, struct cifsSesInfo, |
256 | atomic_set(&tcon->num_oplock_brks, 0); | 254 | smb_ses_list); |
257 | atomic_set(&tcon->num_opens, 0); | 255 | list_for_each(tmp3, &ses->tcon_list) { |
258 | atomic_set(&tcon->num_closes, 0); | 256 | tcon = list_entry(tmp3, |
259 | atomic_set(&tcon->num_deletes, 0); | 257 | struct cifsTconInfo, |
260 | atomic_set(&tcon->num_mkdirs, 0); | 258 | tcon_list); |
261 | atomic_set(&tcon->num_rmdirs, 0); | 259 | atomic_set(&tcon->num_smbs_sent, 0); |
262 | atomic_set(&tcon->num_renames, 0); | 260 | atomic_set(&tcon->num_writes, 0); |
263 | atomic_set(&tcon->num_t2renames, 0); | 261 | atomic_set(&tcon->num_reads, 0); |
264 | atomic_set(&tcon->num_ffirst, 0); | 262 | atomic_set(&tcon->num_oplock_brks, 0); |
265 | atomic_set(&tcon->num_fnext, 0); | 263 | atomic_set(&tcon->num_opens, 0); |
266 | atomic_set(&tcon->num_fclose, 0); | 264 | atomic_set(&tcon->num_closes, 0); |
267 | atomic_set(&tcon->num_hardlinks, 0); | 265 | atomic_set(&tcon->num_deletes, 0); |
268 | atomic_set(&tcon->num_symlinks, 0); | 266 | atomic_set(&tcon->num_mkdirs, 0); |
269 | atomic_set(&tcon->num_locks, 0); | 267 | atomic_set(&tcon->num_rmdirs, 0); |
268 | atomic_set(&tcon->num_renames, 0); | ||
269 | atomic_set(&tcon->num_t2renames, 0); | ||
270 | atomic_set(&tcon->num_ffirst, 0); | ||
271 | atomic_set(&tcon->num_fnext, 0); | ||
272 | atomic_set(&tcon->num_fclose, 0); | ||
273 | atomic_set(&tcon->num_hardlinks, 0); | ||
274 | atomic_set(&tcon->num_symlinks, 0); | ||
275 | atomic_set(&tcon->num_locks, 0); | ||
276 | } | ||
277 | } | ||
270 | } | 278 | } |
271 | read_unlock(&GlobalSMBSeslock); | 279 | read_unlock(&cifs_tcp_ses_lock); |
272 | } | 280 | } |
273 | 281 | ||
274 | return count; | 282 | return count; |
@@ -277,7 +285,9 @@ static ssize_t cifs_stats_proc_write(struct file *file, | |||
277 | static int cifs_stats_proc_show(struct seq_file *m, void *v) | 285 | static int cifs_stats_proc_show(struct seq_file *m, void *v) |
278 | { | 286 | { |
279 | int i; | 287 | int i; |
280 | struct list_head *tmp; | 288 | struct list_head *tmp1, *tmp2, *tmp3; |
289 | struct TCP_Server_Info *server; | ||
290 | struct cifsSesInfo *ses; | ||
281 | struct cifsTconInfo *tcon; | 291 | struct cifsTconInfo *tcon; |
282 | 292 | ||
283 | seq_printf(m, | 293 | seq_printf(m, |
@@ -306,44 +316,55 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) | |||
306 | GlobalCurrentXid, GlobalMaxActiveXid); | 316 | GlobalCurrentXid, GlobalMaxActiveXid); |
307 | 317 | ||
308 | i = 0; | 318 | i = 0; |
309 | read_lock(&GlobalSMBSeslock); | 319 | read_lock(&cifs_tcp_ses_lock); |
310 | list_for_each(tmp, &GlobalTreeConnectionList) { | 320 | list_for_each(tmp1, &cifs_tcp_ses_list) { |
311 | i++; | 321 | server = list_entry(tmp1, struct TCP_Server_Info, |
312 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | 322 | tcp_ses_list); |
313 | seq_printf(m, "\n%d) %s", i, tcon->treeName); | 323 | list_for_each(tmp2, &server->smb_ses_list) { |
314 | if (tcon->tidStatus == CifsNeedReconnect) | 324 | ses = list_entry(tmp2, struct cifsSesInfo, |
315 | seq_puts(m, "\tDISCONNECTED "); | 325 | smb_ses_list); |
316 | seq_printf(m, "\nSMBs: %d Oplock Breaks: %d", | 326 | list_for_each(tmp3, &ses->tcon_list) { |
317 | atomic_read(&tcon->num_smbs_sent), | 327 | tcon = list_entry(tmp3, |
318 | atomic_read(&tcon->num_oplock_brks)); | 328 | struct cifsTconInfo, |
319 | seq_printf(m, "\nReads: %d Bytes: %lld", | 329 | tcon_list); |
320 | atomic_read(&tcon->num_reads), | 330 | i++; |
321 | (long long)(tcon->bytes_read)); | 331 | seq_printf(m, "\n%d) %s", i, tcon->treeName); |
322 | seq_printf(m, "\nWrites: %d Bytes: %lld", | 332 | if (tcon->need_reconnect) |
323 | atomic_read(&tcon->num_writes), | 333 | seq_puts(m, "\tDISCONNECTED "); |
324 | (long long)(tcon->bytes_written)); | 334 | seq_printf(m, "\nSMBs: %d Oplock Breaks: %d", |
325 | seq_printf(m, | 335 | atomic_read(&tcon->num_smbs_sent), |
326 | "\nLocks: %d HardLinks: %d Symlinks: %d", | 336 | atomic_read(&tcon->num_oplock_brks)); |
327 | atomic_read(&tcon->num_locks), | 337 | seq_printf(m, "\nReads: %d Bytes: %lld", |
328 | atomic_read(&tcon->num_hardlinks), | 338 | atomic_read(&tcon->num_reads), |
329 | atomic_read(&tcon->num_symlinks)); | 339 | (long long)(tcon->bytes_read)); |
330 | 340 | seq_printf(m, "\nWrites: %d Bytes: %lld", | |
331 | seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", | 341 | atomic_read(&tcon->num_writes), |
332 | atomic_read(&tcon->num_opens), | 342 | (long long)(tcon->bytes_written)); |
333 | atomic_read(&tcon->num_closes), | 343 | seq_printf(m, "\nLocks: %d HardLinks: %d " |
334 | atomic_read(&tcon->num_deletes)); | 344 | "Symlinks: %d", |
335 | seq_printf(m, "\nMkdirs: %d Rmdirs: %d", | 345 | atomic_read(&tcon->num_locks), |
336 | atomic_read(&tcon->num_mkdirs), | 346 | atomic_read(&tcon->num_hardlinks), |
337 | atomic_read(&tcon->num_rmdirs)); | 347 | atomic_read(&tcon->num_symlinks)); |
338 | seq_printf(m, "\nRenames: %d T2 Renames %d", | 348 | seq_printf(m, "\nOpens: %d Closes: %d" |
339 | atomic_read(&tcon->num_renames), | 349 | "Deletes: %d", |
340 | atomic_read(&tcon->num_t2renames)); | 350 | atomic_read(&tcon->num_opens), |
341 | seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", | 351 | atomic_read(&tcon->num_closes), |
342 | atomic_read(&tcon->num_ffirst), | 352 | atomic_read(&tcon->num_deletes)); |
343 | atomic_read(&tcon->num_fnext), | 353 | seq_printf(m, "\nMkdirs: %d Rmdirs: %d", |
344 | atomic_read(&tcon->num_fclose)); | 354 | atomic_read(&tcon->num_mkdirs), |
355 | atomic_read(&tcon->num_rmdirs)); | ||
356 | seq_printf(m, "\nRenames: %d T2 Renames %d", | ||
357 | atomic_read(&tcon->num_renames), | ||
358 | atomic_read(&tcon->num_t2renames)); | ||
359 | seq_printf(m, "\nFindFirst: %d FNext %d " | ||
360 | "FClose %d", | ||
361 | atomic_read(&tcon->num_ffirst), | ||
362 | atomic_read(&tcon->num_fnext), | ||
363 | atomic_read(&tcon->num_fclose)); | ||
364 | } | ||
365 | } | ||
345 | } | 366 | } |
346 | read_unlock(&GlobalSMBSeslock); | 367 | read_unlock(&cifs_tcp_ses_lock); |
347 | 368 | ||
348 | seq_putc(m, '\n'); | 369 | seq_putc(m, '\n'); |
349 | return 0; | 370 | return 0; |
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index d2c8eef84f3c..e1c18362ba46 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c | |||
@@ -106,7 +106,8 @@ static char *cifs_get_share_name(const char *node_name) | |||
106 | /** | 106 | /** |
107 | * compose_mount_options - creates mount options for refferral | 107 | * compose_mount_options - creates mount options for refferral |
108 | * @sb_mountdata: parent/root DFS mount options (template) | 108 | * @sb_mountdata: parent/root DFS mount options (template) |
109 | * @ref_unc: refferral server UNC | 109 | * @dentry: point where we are going to mount |
110 | * @ref: server's referral | ||
110 | * @devname: pointer for saving device name | 111 | * @devname: pointer for saving device name |
111 | * | 112 | * |
112 | * creates mount options for submount based on template options sb_mountdata | 113 | * creates mount options for submount based on template options sb_mountdata |
@@ -116,7 +117,8 @@ static char *cifs_get_share_name(const char *node_name) | |||
116 | * Caller is responcible for freeing retunrned value if it is not error. | 117 | * Caller is responcible for freeing retunrned value if it is not error. |
117 | */ | 118 | */ |
118 | static char *compose_mount_options(const char *sb_mountdata, | 119 | static char *compose_mount_options(const char *sb_mountdata, |
119 | const char *ref_unc, | 120 | struct dentry *dentry, |
121 | const struct dfs_info3_param *ref, | ||
120 | char **devname) | 122 | char **devname) |
121 | { | 123 | { |
122 | int rc; | 124 | int rc; |
@@ -126,11 +128,12 @@ static char *compose_mount_options(const char *sb_mountdata, | |||
126 | char *srvIP = NULL; | 128 | char *srvIP = NULL; |
127 | char sep = ','; | 129 | char sep = ','; |
128 | int off, noff; | 130 | int off, noff; |
131 | char *fullpath; | ||
129 | 132 | ||
130 | if (sb_mountdata == NULL) | 133 | if (sb_mountdata == NULL) |
131 | return ERR_PTR(-EINVAL); | 134 | return ERR_PTR(-EINVAL); |
132 | 135 | ||
133 | *devname = cifs_get_share_name(ref_unc); | 136 | *devname = cifs_get_share_name(ref->node_name); |
134 | rc = dns_resolve_server_name_to_ip(*devname, &srvIP); | 137 | rc = dns_resolve_server_name_to_ip(*devname, &srvIP); |
135 | if (rc != 0) { | 138 | if (rc != 0) { |
136 | cERROR(1, ("%s: Failed to resolve server part of %s to IP", | 139 | cERROR(1, ("%s: Failed to resolve server part of %s to IP", |
@@ -138,7 +141,12 @@ static char *compose_mount_options(const char *sb_mountdata, | |||
138 | mountdata = ERR_PTR(rc); | 141 | mountdata = ERR_PTR(rc); |
139 | goto compose_mount_options_out; | 142 | goto compose_mount_options_out; |
140 | } | 143 | } |
141 | md_len = strlen(sb_mountdata) + strlen(srvIP) + strlen(ref_unc) + 3; | 144 | /* md_len = strlen(...) + 12 for 'sep+prefixpath=' |
145 | * assuming that we have 'unc=' and 'ip=' in | ||
146 | * the original sb_mountdata | ||
147 | */ | ||
148 | md_len = strlen(sb_mountdata) + strlen(srvIP) + | ||
149 | strlen(ref->node_name) + 12; | ||
142 | mountdata = kzalloc(md_len+1, GFP_KERNEL); | 150 | mountdata = kzalloc(md_len+1, GFP_KERNEL); |
143 | if (mountdata == NULL) { | 151 | if (mountdata == NULL) { |
144 | mountdata = ERR_PTR(-ENOMEM); | 152 | mountdata = ERR_PTR(-ENOMEM); |
@@ -152,41 +160,56 @@ static char *compose_mount_options(const char *sb_mountdata, | |||
152 | strncpy(mountdata, sb_mountdata, 5); | 160 | strncpy(mountdata, sb_mountdata, 5); |
153 | off += 5; | 161 | off += 5; |
154 | } | 162 | } |
155 | while ((tkn_e = strchr(sb_mountdata+off, sep))) { | 163 | |
156 | noff = (tkn_e - (sb_mountdata+off)) + 1; | 164 | do { |
157 | if (strnicmp(sb_mountdata+off, "unc=", 4) == 0) { | 165 | tkn_e = strchr(sb_mountdata + off, sep); |
166 | if (tkn_e == NULL) | ||
167 | noff = strlen(sb_mountdata + off); | ||
168 | else | ||
169 | noff = tkn_e - (sb_mountdata + off) + 1; | ||
170 | |||
171 | if (strnicmp(sb_mountdata + off, "unc=", 4) == 0) { | ||
158 | off += noff; | 172 | off += noff; |
159 | continue; | 173 | continue; |
160 | } | 174 | } |
161 | if (strnicmp(sb_mountdata+off, "ip=", 3) == 0) { | 175 | if (strnicmp(sb_mountdata + off, "ip=", 3) == 0) { |
162 | off += noff; | 176 | off += noff; |
163 | continue; | 177 | continue; |
164 | } | 178 | } |
165 | if (strnicmp(sb_mountdata+off, "prefixpath=", 3) == 0) { | 179 | if (strnicmp(sb_mountdata + off, "prefixpath=", 11) == 0) { |
166 | off += noff; | 180 | off += noff; |
167 | continue; | 181 | continue; |
168 | } | 182 | } |
169 | strncat(mountdata, sb_mountdata+off, noff); | 183 | strncat(mountdata, sb_mountdata + off, noff); |
170 | off += noff; | 184 | off += noff; |
171 | } | 185 | } while (tkn_e); |
172 | strcat(mountdata, sb_mountdata+off); | 186 | strcat(mountdata, sb_mountdata + off); |
173 | mountdata[md_len] = '\0'; | 187 | mountdata[md_len] = '\0'; |
174 | 188 | ||
175 | /* copy new IP and ref share name */ | 189 | /* copy new IP and ref share name */ |
176 | strcat(mountdata, ",ip="); | 190 | if (mountdata[strlen(mountdata) - 1] != sep) |
191 | strncat(mountdata, &sep, 1); | ||
192 | strcat(mountdata, "ip="); | ||
177 | strcat(mountdata, srvIP); | 193 | strcat(mountdata, srvIP); |
178 | strcat(mountdata, ",unc="); | 194 | strncat(mountdata, &sep, 1); |
195 | strcat(mountdata, "unc="); | ||
179 | strcat(mountdata, *devname); | 196 | strcat(mountdata, *devname); |
180 | 197 | ||
181 | /* find & copy prefixpath */ | 198 | /* find & copy prefixpath */ |
182 | tkn_e = strchr(ref_unc+2, '\\'); | 199 | tkn_e = strchr(ref->node_name + 2, '\\'); |
183 | if (tkn_e) { | 200 | if (tkn_e == NULL) /* invalid unc, missing share name*/ |
184 | tkn_e = strchr(tkn_e+1, '\\'); | 201 | goto compose_mount_options_out; |
185 | if (tkn_e) { | 202 | |
186 | strcat(mountdata, ",prefixpath="); | 203 | fullpath = build_path_from_dentry(dentry); |
187 | strcat(mountdata, tkn_e+1); | 204 | tkn_e = strchr(tkn_e + 1, '\\'); |
188 | } | 205 | if (tkn_e || strlen(fullpath) - (ref->path_consumed)) { |
206 | strncat(mountdata, &sep, 1); | ||
207 | strcat(mountdata, "prefixpath="); | ||
208 | if (tkn_e) | ||
209 | strcat(mountdata, tkn_e + 1); | ||
210 | strcat(mountdata, fullpath + (ref->path_consumed)); | ||
189 | } | 211 | } |
212 | kfree(fullpath); | ||
190 | 213 | ||
191 | /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/ | 214 | /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/ |
192 | /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/ | 215 | /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/ |
@@ -198,7 +221,7 @@ compose_mount_options_out: | |||
198 | 221 | ||
199 | 222 | ||
200 | static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, | 223 | static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, |
201 | struct dentry *dentry, char *ref_unc) | 224 | struct dentry *dentry, const struct dfs_info3_param *ref) |
202 | { | 225 | { |
203 | struct cifs_sb_info *cifs_sb; | 226 | struct cifs_sb_info *cifs_sb; |
204 | struct vfsmount *mnt; | 227 | struct vfsmount *mnt; |
@@ -207,7 +230,7 @@ static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, | |||
207 | 230 | ||
208 | cifs_sb = CIFS_SB(dentry->d_inode->i_sb); | 231 | cifs_sb = CIFS_SB(dentry->d_inode->i_sb); |
209 | mountdata = compose_mount_options(cifs_sb->mountdata, | 232 | mountdata = compose_mount_options(cifs_sb->mountdata, |
210 | ref_unc, &devname); | 233 | dentry, ref, &devname); |
211 | 234 | ||
212 | if (IS_ERR(mountdata)) | 235 | if (IS_ERR(mountdata)) |
213 | return (struct vfsmount *)mountdata; | 236 | return (struct vfsmount *)mountdata; |
@@ -310,7 +333,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | |||
310 | } | 333 | } |
311 | mnt = cifs_dfs_do_refmount(nd->path.mnt, | 334 | mnt = cifs_dfs_do_refmount(nd->path.mnt, |
312 | nd->path.dentry, | 335 | nd->path.dentry, |
313 | referrals[i].node_name); | 336 | referrals + i); |
314 | cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", | 337 | cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", |
315 | __func__, | 338 | __func__, |
316 | referrals[i].node_name, mnt)); | 339 | referrals[i].node_name, mnt)); |
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index fcee9298b620..0ab2fb5afef1 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c | |||
@@ -73,8 +73,8 @@ struct key_type cifs_spnego_key_type = { | |||
73 | * strlen(";sec=ntlmsspi") */ | 73 | * strlen(";sec=ntlmsspi") */ |
74 | #define MAX_MECH_STR_LEN 13 | 74 | #define MAX_MECH_STR_LEN 13 |
75 | 75 | ||
76 | /* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */ | 76 | /* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/128 */ |
77 | #define MAX_IPV6_ADDR_LEN 42 | 77 | #define MAX_IPV6_ADDR_LEN 43 |
78 | 78 | ||
79 | /* strlen of "host=" */ | 79 | /* strlen of "host=" */ |
80 | #define HOST_KEY_LEN 5 | 80 | #define HOST_KEY_LEN 5 |
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index ac5915d61dca..d9cf467309e8 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -514,10 +514,11 @@ static void cifs_umount_begin(struct super_block *sb) | |||
514 | tcon = cifs_sb->tcon; | 514 | tcon = cifs_sb->tcon; |
515 | if (tcon == NULL) | 515 | if (tcon == NULL) |
516 | return; | 516 | return; |
517 | down(&tcon->tconSem); | 517 | |
518 | if (atomic_read(&tcon->useCount) == 1) | 518 | read_lock(&cifs_tcp_ses_lock); |
519 | if (tcon->tc_count == 1) | ||
519 | tcon->tidStatus = CifsExiting; | 520 | tcon->tidStatus = CifsExiting; |
520 | up(&tcon->tconSem); | 521 | read_unlock(&cifs_tcp_ses_lock); |
521 | 522 | ||
522 | /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ | 523 | /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ |
523 | /* cancel_notify_requests(tcon); */ | 524 | /* cancel_notify_requests(tcon); */ |
@@ -1013,7 +1014,7 @@ static int cifs_oplock_thread(void *dummyarg) | |||
1013 | not bother sending an oplock release if session | 1014 | not bother sending an oplock release if session |
1014 | to server still is disconnected since oplock | 1015 | to server still is disconnected since oplock |
1015 | already released by the server in that case */ | 1016 | already released by the server in that case */ |
1016 | if (pTcon->tidStatus != CifsNeedReconnect) { | 1017 | if (!pTcon->need_reconnect) { |
1017 | rc = CIFSSMBLock(0, pTcon, netfid, | 1018 | rc = CIFSSMBLock(0, pTcon, netfid, |
1018 | 0 /* len */ , 0 /* offset */, 0, | 1019 | 0 /* len */ , 0 /* offset */, 0, |
1019 | 0, LOCKING_ANDX_OPLOCK_RELEASE, | 1020 | 0, LOCKING_ANDX_OPLOCK_RELEASE, |
@@ -1031,24 +1032,24 @@ static int cifs_oplock_thread(void *dummyarg) | |||
1031 | static int cifs_dnotify_thread(void *dummyarg) | 1032 | static int cifs_dnotify_thread(void *dummyarg) |
1032 | { | 1033 | { |
1033 | struct list_head *tmp; | 1034 | struct list_head *tmp; |
1034 | struct cifsSesInfo *ses; | 1035 | struct TCP_Server_Info *server; |
1035 | 1036 | ||
1036 | do { | 1037 | do { |
1037 | if (try_to_freeze()) | 1038 | if (try_to_freeze()) |
1038 | continue; | 1039 | continue; |
1039 | set_current_state(TASK_INTERRUPTIBLE); | 1040 | set_current_state(TASK_INTERRUPTIBLE); |
1040 | schedule_timeout(15*HZ); | 1041 | schedule_timeout(15*HZ); |
1041 | read_lock(&GlobalSMBSeslock); | ||
1042 | /* check if any stuck requests that need | 1042 | /* check if any stuck requests that need |
1043 | to be woken up and wakeq so the | 1043 | to be woken up and wakeq so the |
1044 | thread can wake up and error out */ | 1044 | thread can wake up and error out */ |
1045 | list_for_each(tmp, &GlobalSMBSessionList) { | 1045 | read_lock(&cifs_tcp_ses_lock); |
1046 | ses = list_entry(tmp, struct cifsSesInfo, | 1046 | list_for_each(tmp, &cifs_tcp_ses_list) { |
1047 | cifsSessionList); | 1047 | server = list_entry(tmp, struct TCP_Server_Info, |
1048 | if (ses->server && atomic_read(&ses->server->inFlight)) | 1048 | tcp_ses_list); |
1049 | wake_up_all(&ses->server->response_q); | 1049 | if (atomic_read(&server->inFlight)) |
1050 | wake_up_all(&server->response_q); | ||
1050 | } | 1051 | } |
1051 | read_unlock(&GlobalSMBSeslock); | 1052 | read_unlock(&cifs_tcp_ses_lock); |
1052 | } while (!kthread_should_stop()); | 1053 | } while (!kthread_should_stop()); |
1053 | 1054 | ||
1054 | return 0; | 1055 | return 0; |
@@ -1059,9 +1060,7 @@ init_cifs(void) | |||
1059 | { | 1060 | { |
1060 | int rc = 0; | 1061 | int rc = 0; |
1061 | cifs_proc_init(); | 1062 | cifs_proc_init(); |
1062 | /* INIT_LIST_HEAD(&GlobalServerList);*/ /* BB not implemented yet */ | 1063 | INIT_LIST_HEAD(&cifs_tcp_ses_list); |
1063 | INIT_LIST_HEAD(&GlobalSMBSessionList); | ||
1064 | INIT_LIST_HEAD(&GlobalTreeConnectionList); | ||
1065 | INIT_LIST_HEAD(&GlobalOplock_Q); | 1064 | INIT_LIST_HEAD(&GlobalOplock_Q); |
1066 | #ifdef CONFIG_CIFS_EXPERIMENTAL | 1065 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
1067 | INIT_LIST_HEAD(&GlobalDnotifyReqList); | 1066 | INIT_LIST_HEAD(&GlobalDnotifyReqList); |
@@ -1089,6 +1088,7 @@ init_cifs(void) | |||
1089 | GlobalMaxActiveXid = 0; | 1088 | GlobalMaxActiveXid = 0; |
1090 | memset(Local_System_Name, 0, 15); | 1089 | memset(Local_System_Name, 0, 15); |
1091 | rwlock_init(&GlobalSMBSeslock); | 1090 | rwlock_init(&GlobalSMBSeslock); |
1091 | rwlock_init(&cifs_tcp_ses_lock); | ||
1092 | spin_lock_init(&GlobalMid_Lock); | 1092 | spin_lock_init(&GlobalMid_Lock); |
1093 | 1093 | ||
1094 | if (cifs_max_pending < 2) { | 1094 | if (cifs_max_pending < 2) { |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1cb1189f24e0..f1ae1f57c30d 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -85,8 +85,7 @@ enum securityEnum { | |||
85 | }; | 85 | }; |
86 | 86 | ||
87 | enum protocolEnum { | 87 | enum protocolEnum { |
88 | IPV4 = 0, | 88 | TCP = 0, |
89 | IPV6, | ||
90 | SCTP | 89 | SCTP |
91 | /* Netbios frames protocol not supported at this time */ | 90 | /* Netbios frames protocol not supported at this time */ |
92 | }; | 91 | }; |
@@ -122,6 +121,9 @@ struct cifs_cred { | |||
122 | */ | 121 | */ |
123 | 122 | ||
124 | struct TCP_Server_Info { | 123 | struct TCP_Server_Info { |
124 | struct list_head tcp_ses_list; | ||
125 | struct list_head smb_ses_list; | ||
126 | int srv_count; /* reference counter */ | ||
125 | /* 15 character server name + 0x20 16th byte indicating type = srv */ | 127 | /* 15 character server name + 0x20 16th byte indicating type = srv */ |
126 | char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; | 128 | char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; |
127 | char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; | 129 | char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; |
@@ -143,7 +145,6 @@ struct TCP_Server_Info { | |||
143 | bool svlocal:1; /* local server or remote */ | 145 | bool svlocal:1; /* local server or remote */ |
144 | bool noblocksnd; /* use blocking sendmsg */ | 146 | bool noblocksnd; /* use blocking sendmsg */ |
145 | bool noautotune; /* do not autotune send buf sizes */ | 147 | bool noautotune; /* do not autotune send buf sizes */ |
146 | atomic_t socketUseCount; /* number of open cifs sessions on socket */ | ||
147 | atomic_t inFlight; /* number of requests on the wire to server */ | 148 | atomic_t inFlight; /* number of requests on the wire to server */ |
148 | #ifdef CONFIG_CIFS_STATS2 | 149 | #ifdef CONFIG_CIFS_STATS2 |
149 | atomic_t inSend; /* requests trying to send */ | 150 | atomic_t inSend; /* requests trying to send */ |
@@ -194,13 +195,14 @@ struct cifsUidInfo { | |||
194 | * Session structure. One of these for each uid session with a particular host | 195 | * Session structure. One of these for each uid session with a particular host |
195 | */ | 196 | */ |
196 | struct cifsSesInfo { | 197 | struct cifsSesInfo { |
197 | struct list_head cifsSessionList; | 198 | struct list_head smb_ses_list; |
199 | struct list_head tcon_list; | ||
198 | struct semaphore sesSem; | 200 | struct semaphore sesSem; |
199 | #if 0 | 201 | #if 0 |
200 | struct cifsUidInfo *uidInfo; /* pointer to user info */ | 202 | struct cifsUidInfo *uidInfo; /* pointer to user info */ |
201 | #endif | 203 | #endif |
202 | struct TCP_Server_Info *server; /* pointer to server info */ | 204 | struct TCP_Server_Info *server; /* pointer to server info */ |
203 | atomic_t inUse; /* # of mounts (tree connections) on this ses */ | 205 | int ses_count; /* reference counter */ |
204 | enum statusEnum status; | 206 | enum statusEnum status; |
205 | unsigned overrideSecFlg; /* if non-zero override global sec flags */ | 207 | unsigned overrideSecFlg; /* if non-zero override global sec flags */ |
206 | __u16 ipc_tid; /* special tid for connection to IPC share */ | 208 | __u16 ipc_tid; /* special tid for connection to IPC share */ |
@@ -216,6 +218,7 @@ struct cifsSesInfo { | |||
216 | char userName[MAX_USERNAME_SIZE + 1]; | 218 | char userName[MAX_USERNAME_SIZE + 1]; |
217 | char *domainName; | 219 | char *domainName; |
218 | char *password; | 220 | char *password; |
221 | bool need_reconnect:1; /* connection reset, uid now invalid */ | ||
219 | }; | 222 | }; |
220 | /* no more than one of the following three session flags may be set */ | 223 | /* no more than one of the following three session flags may be set */ |
221 | #define CIFS_SES_NT4 1 | 224 | #define CIFS_SES_NT4 1 |
@@ -230,16 +233,15 @@ struct cifsSesInfo { | |||
230 | * session | 233 | * session |
231 | */ | 234 | */ |
232 | struct cifsTconInfo { | 235 | struct cifsTconInfo { |
233 | struct list_head cifsConnectionList; | 236 | struct list_head tcon_list; |
237 | int tc_count; | ||
234 | struct list_head openFileList; | 238 | struct list_head openFileList; |
235 | struct semaphore tconSem; | ||
236 | struct cifsSesInfo *ses; /* pointer to session associated with */ | 239 | struct cifsSesInfo *ses; /* pointer to session associated with */ |
237 | char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ | 240 | char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ |
238 | char *nativeFileSystem; | 241 | char *nativeFileSystem; |
239 | __u16 tid; /* The 2 byte tree id */ | 242 | __u16 tid; /* The 2 byte tree id */ |
240 | __u16 Flags; /* optional support bits */ | 243 | __u16 Flags; /* optional support bits */ |
241 | enum statusEnum tidStatus; | 244 | enum statusEnum tidStatus; |
242 | atomic_t useCount; /* how many explicit/implicit mounts to share */ | ||
243 | #ifdef CONFIG_CIFS_STATS | 245 | #ifdef CONFIG_CIFS_STATS |
244 | atomic_t num_smbs_sent; | 246 | atomic_t num_smbs_sent; |
245 | atomic_t num_writes; | 247 | atomic_t num_writes; |
@@ -288,6 +290,7 @@ struct cifsTconInfo { | |||
288 | bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol | 290 | bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol |
289 | for this mount even if server would support */ | 291 | for this mount even if server would support */ |
290 | bool local_lease:1; /* check leases (only) on local system not remote */ | 292 | bool local_lease:1; /* check leases (only) on local system not remote */ |
293 | bool need_reconnect:1; /* connection reset, tid now invalid */ | ||
291 | /* BB add field for back pointer to sb struct(s)? */ | 294 | /* BB add field for back pointer to sb struct(s)? */ |
292 | }; | 295 | }; |
293 | 296 | ||
@@ -588,21 +591,21 @@ require use of the stronger protocol */ | |||
588 | #endif | 591 | #endif |
589 | 592 | ||
590 | /* | 593 | /* |
591 | * The list of servers that did not respond with NT LM 0.12. | 594 | * the list of TCP_Server_Info structures, ie each of the sockets |
592 | * This list helps improve performance and eliminate the messages indicating | 595 | * connecting our client to a distinct server (ip address), is |
593 | * that we had a communications error talking to the server in this list. | 596 | * chained together by cifs_tcp_ses_list. The list of all our SMB |
597 | * sessions (and from that the tree connections) can be found | ||
598 | * by iterating over cifs_tcp_ses_list | ||
594 | */ | 599 | */ |
595 | /* Feature not supported */ | 600 | GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; |
596 | /* GLOBAL_EXTERN struct servers_not_supported *NotSuppList; */ | ||
597 | 601 | ||
598 | /* | 602 | /* |
599 | * The following is a hash table of all the users we know about. | 603 | * This lock protects the cifs_tcp_ses_list, the list of smb sessions per |
604 | * tcp session, and the list of tcon's per smb session. It also protects | ||
605 | * the reference counters for the server, smb session, and tcon. Finally, | ||
606 | * changes to the tcon->tidStatus should be done while holding this lock. | ||
600 | */ | 607 | */ |
601 | GLOBAL_EXTERN struct smbUidInfo *GlobalUidList[UID_HASH]; | 608 | GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; |
602 | |||
603 | /* GLOBAL_EXTERN struct list_head GlobalServerList; BB not implemented yet */ | ||
604 | GLOBAL_EXTERN struct list_head GlobalSMBSessionList; | ||
605 | GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; | ||
606 | GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ | 609 | GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ |
607 | 610 | ||
608 | GLOBAL_EXTERN struct list_head GlobalOplock_Q; | 611 | GLOBAL_EXTERN struct list_head GlobalOplock_Q; |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index d5eac48fc415..bdda46dd435a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -190,10 +190,10 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, | |||
190 | /* need to prevent multiple threads trying to | 190 | /* need to prevent multiple threads trying to |
191 | simultaneously reconnect the same SMB session */ | 191 | simultaneously reconnect the same SMB session */ |
192 | down(&tcon->ses->sesSem); | 192 | down(&tcon->ses->sesSem); |
193 | if (tcon->ses->status == CifsNeedReconnect) | 193 | if (tcon->ses->need_reconnect) |
194 | rc = cifs_setup_session(0, tcon->ses, | 194 | rc = cifs_setup_session(0, tcon->ses, |
195 | nls_codepage); | 195 | nls_codepage); |
196 | if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { | 196 | if (!rc && (tcon->need_reconnect)) { |
197 | mark_open_files_invalid(tcon); | 197 | mark_open_files_invalid(tcon); |
198 | rc = CIFSTCon(0, tcon->ses, tcon->treeName, | 198 | rc = CIFSTCon(0, tcon->ses, tcon->treeName, |
199 | tcon, nls_codepage); | 199 | tcon, nls_codepage); |
@@ -295,7 +295,7 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, | |||
295 | check for tcp and smb session status done differently | 295 | check for tcp and smb session status done differently |
296 | for those three - in the calling routine */ | 296 | for those three - in the calling routine */ |
297 | if (tcon) { | 297 | if (tcon) { |
298 | if (tcon->tidStatus == CifsExiting) { | 298 | if (tcon->need_reconnect) { |
299 | /* only tree disconnect, open, and write, | 299 | /* only tree disconnect, open, and write, |
300 | (and ulogoff which does not have tcon) | 300 | (and ulogoff which does not have tcon) |
301 | are allowed as we start force umount */ | 301 | are allowed as we start force umount */ |
@@ -337,10 +337,10 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, | |||
337 | /* need to prevent multiple threads trying to | 337 | /* need to prevent multiple threads trying to |
338 | simultaneously reconnect the same SMB session */ | 338 | simultaneously reconnect the same SMB session */ |
339 | down(&tcon->ses->sesSem); | 339 | down(&tcon->ses->sesSem); |
340 | if (tcon->ses->status == CifsNeedReconnect) | 340 | if (tcon->ses->need_reconnect) |
341 | rc = cifs_setup_session(0, tcon->ses, | 341 | rc = cifs_setup_session(0, tcon->ses, |
342 | nls_codepage); | 342 | nls_codepage); |
343 | if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { | 343 | if (!rc && (tcon->need_reconnect)) { |
344 | mark_open_files_invalid(tcon); | 344 | mark_open_files_invalid(tcon); |
345 | rc = CIFSTCon(0, tcon->ses, tcon->treeName, | 345 | rc = CIFSTCon(0, tcon->ses, tcon->treeName, |
346 | tcon, nls_codepage); | 346 | tcon, nls_codepage); |
@@ -664,8 +664,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) | |||
664 | rc = -EIO; | 664 | rc = -EIO; |
665 | goto neg_err_exit; | 665 | goto neg_err_exit; |
666 | } | 666 | } |
667 | 667 | read_lock(&cifs_tcp_ses_lock); | |
668 | if (server->socketUseCount.counter > 1) { | 668 | if (server->srv_count > 1) { |
669 | read_unlock(&cifs_tcp_ses_lock); | ||
669 | if (memcmp(server->server_GUID, | 670 | if (memcmp(server->server_GUID, |
670 | pSMBr->u.extended_response. | 671 | pSMBr->u.extended_response. |
671 | GUID, 16) != 0) { | 672 | GUID, 16) != 0) { |
@@ -674,9 +675,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) | |||
674 | pSMBr->u.extended_response.GUID, | 675 | pSMBr->u.extended_response.GUID, |
675 | 16); | 676 | 16); |
676 | } | 677 | } |
677 | } else | 678 | } else { |
679 | read_unlock(&cifs_tcp_ses_lock); | ||
678 | memcpy(server->server_GUID, | 680 | memcpy(server->server_GUID, |
679 | pSMBr->u.extended_response.GUID, 16); | 681 | pSMBr->u.extended_response.GUID, 16); |
682 | } | ||
680 | 683 | ||
681 | if (count == 16) { | 684 | if (count == 16) { |
682 | server->secType = RawNTLMSSP; | 685 | server->secType = RawNTLMSSP; |
@@ -739,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) | |||
739 | int rc = 0; | 742 | int rc = 0; |
740 | 743 | ||
741 | cFYI(1, ("In tree disconnect")); | 744 | cFYI(1, ("In tree disconnect")); |
742 | /* | ||
743 | * If last user of the connection and | ||
744 | * connection alive - disconnect it | ||
745 | * If this is the last connection on the server session disconnect it | ||
746 | * (and inside session disconnect we should check if tcp socket needs | ||
747 | * to be freed and kernel thread woken up). | ||
748 | */ | ||
749 | if (tcon) | ||
750 | down(&tcon->tconSem); | ||
751 | else | ||
752 | return -EIO; | ||
753 | 745 | ||
754 | atomic_dec(&tcon->useCount); | 746 | /* BB: do we need to check this? These should never be NULL. */ |
755 | if (atomic_read(&tcon->useCount) > 0) { | 747 | if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) |
756 | up(&tcon->tconSem); | 748 | return -EIO; |
757 | return -EBUSY; | ||
758 | } | ||
759 | 749 | ||
760 | /* No need to return error on this operation if tid invalidated and | 750 | /* |
761 | closed on server already e.g. due to tcp session crashing */ | 751 | * No need to return error on this operation if tid invalidated and |
762 | if (tcon->tidStatus == CifsNeedReconnect) { | 752 | * closed on server already e.g. due to tcp session crashing. Also, |
763 | up(&tcon->tconSem); | 753 | * the tcon is no longer on the list, so no need to take lock before |
754 | * checking this. | ||
755 | */ | ||
756 | if (tcon->need_reconnect) | ||
764 | return 0; | 757 | return 0; |
765 | } | ||
766 | 758 | ||
767 | if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) { | ||
768 | up(&tcon->tconSem); | ||
769 | return -EIO; | ||
770 | } | ||
771 | rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, | 759 | rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, |
772 | (void **)&smb_buffer); | 760 | (void **)&smb_buffer); |
773 | if (rc) { | 761 | if (rc) |
774 | up(&tcon->tconSem); | ||
775 | return rc; | 762 | return rc; |
776 | } | ||
777 | 763 | ||
778 | rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); | 764 | rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); |
779 | if (rc) | 765 | if (rc) |
780 | cFYI(1, ("Tree disconnect failed %d", rc)); | 766 | cFYI(1, ("Tree disconnect failed %d", rc)); |
781 | 767 | ||
782 | up(&tcon->tconSem); | ||
783 | |||
784 | /* No need to return error on this operation if tid invalidated and | 768 | /* No need to return error on this operation if tid invalidated and |
785 | closed on server already e.g. due to tcp session crashing */ | 769 | closed on server already e.g. due to tcp session crashing */ |
786 | if (rc == -EAGAIN) | 770 | if (rc == -EAGAIN) |
787 | rc = 0; | 771 | rc = 0; |
788 | 772 | ||
@@ -796,43 +780,36 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) | |||
796 | int rc = 0; | 780 | int rc = 0; |
797 | 781 | ||
798 | cFYI(1, ("In SMBLogoff for session disconnect")); | 782 | cFYI(1, ("In SMBLogoff for session disconnect")); |
799 | if (ses) | 783 | |
800 | down(&ses->sesSem); | 784 | /* |
801 | else | 785 | * BB: do we need to check validity of ses and server? They should |
786 | * always be valid since we have an active reference. If not, that | ||
787 | * should probably be a BUG() | ||
788 | */ | ||
789 | if (!ses || !ses->server) | ||
802 | return -EIO; | 790 | return -EIO; |
803 | 791 | ||
804 | atomic_dec(&ses->inUse); | 792 | down(&ses->sesSem); |
805 | if (atomic_read(&ses->inUse) > 0) { | 793 | if (ses->need_reconnect) |
806 | up(&ses->sesSem); | 794 | goto session_already_dead; /* no need to send SMBlogoff if uid |
807 | return -EBUSY; | 795 | already closed due to reconnect */ |
808 | } | ||
809 | rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); | 796 | rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); |
810 | if (rc) { | 797 | if (rc) { |
811 | up(&ses->sesSem); | 798 | up(&ses->sesSem); |
812 | return rc; | 799 | return rc; |
813 | } | 800 | } |
814 | 801 | ||
815 | if (ses->server) { | 802 | pSMB->hdr.Mid = GetNextMid(ses->server); |
816 | pSMB->hdr.Mid = GetNextMid(ses->server); | ||
817 | 803 | ||
818 | if (ses->server->secMode & | 804 | if (ses->server->secMode & |
819 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) | 805 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) |
820 | pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; | 806 | pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; |
821 | } | ||
822 | 807 | ||
823 | pSMB->hdr.Uid = ses->Suid; | 808 | pSMB->hdr.Uid = ses->Suid; |
824 | 809 | ||
825 | pSMB->AndXCommand = 0xFF; | 810 | pSMB->AndXCommand = 0xFF; |
826 | rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); | 811 | rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); |
827 | if (ses->server) { | 812 | session_already_dead: |
828 | atomic_dec(&ses->server->socketUseCount); | ||
829 | if (atomic_read(&ses->server->socketUseCount) == 0) { | ||
830 | spin_lock(&GlobalMid_Lock); | ||
831 | ses->server->tcpStatus = CifsExiting; | ||
832 | spin_unlock(&GlobalMid_Lock); | ||
833 | rc = -ESHUTDOWN; | ||
834 | } | ||
835 | } | ||
836 | up(&ses->sesSem); | 813 | up(&ses->sesSem); |
837 | 814 | ||
838 | /* if session dead then we do not need to do ulogoff, | 815 | /* if session dead then we do not need to do ulogoff, |
@@ -3922,6 +3899,27 @@ GetInodeNumOut: | |||
3922 | return rc; | 3899 | return rc; |
3923 | } | 3900 | } |
3924 | 3901 | ||
3902 | /* computes length of UCS string converted to host codepage | ||
3903 | * @src: UCS string | ||
3904 | * @maxlen: length of the input string in UCS characters | ||
3905 | * (not in bytes) | ||
3906 | * | ||
3907 | * return: size of input string in host codepage | ||
3908 | */ | ||
3909 | static int hostlen_fromUCS(const __le16 *src, const int maxlen, | ||
3910 | const struct nls_table *nls_codepage) { | ||
3911 | int i; | ||
3912 | int hostlen = 0; | ||
3913 | char to[4]; | ||
3914 | int charlen; | ||
3915 | for (i = 0; (i < maxlen) && src[i]; ++i) { | ||
3916 | charlen = nls_codepage->uni2char(le16_to_cpu(src[i]), | ||
3917 | to, NLS_MAX_CHARSET_SIZE); | ||
3918 | hostlen += charlen > 0 ? charlen : 1; | ||
3919 | } | ||
3920 | return hostlen; | ||
3921 | } | ||
3922 | |||
3925 | /* parses DFS refferal V3 structure | 3923 | /* parses DFS refferal V3 structure |
3926 | * caller is responsible for freeing target_nodes | 3924 | * caller is responsible for freeing target_nodes |
3927 | * returns: | 3925 | * returns: |
@@ -3932,7 +3930,8 @@ static int | |||
3932 | parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, | 3930 | parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, |
3933 | unsigned int *num_of_nodes, | 3931 | unsigned int *num_of_nodes, |
3934 | struct dfs_info3_param **target_nodes, | 3932 | struct dfs_info3_param **target_nodes, |
3935 | const struct nls_table *nls_codepage) | 3933 | const struct nls_table *nls_codepage, int remap, |
3934 | const char *searchName) | ||
3936 | { | 3935 | { |
3937 | int i, rc = 0; | 3936 | int i, rc = 0; |
3938 | char *data_end; | 3937 | char *data_end; |
@@ -3983,7 +3982,17 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, | |||
3983 | struct dfs_info3_param *node = (*target_nodes)+i; | 3982 | struct dfs_info3_param *node = (*target_nodes)+i; |
3984 | 3983 | ||
3985 | node->flags = le16_to_cpu(pSMBr->DFSFlags); | 3984 | node->flags = le16_to_cpu(pSMBr->DFSFlags); |
3986 | node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); | 3985 | if (is_unicode) { |
3986 | __le16 *tmp = kmalloc(strlen(searchName)*2, GFP_KERNEL); | ||
3987 | cifsConvertToUCS((__le16 *) tmp, searchName, | ||
3988 | PATH_MAX, nls_codepage, remap); | ||
3989 | node->path_consumed = hostlen_fromUCS(tmp, | ||
3990 | le16_to_cpu(pSMBr->PathConsumed)/2, | ||
3991 | nls_codepage); | ||
3992 | kfree(tmp); | ||
3993 | } else | ||
3994 | node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); | ||
3995 | |||
3987 | node->server_type = le16_to_cpu(ref->ServerType); | 3996 | node->server_type = le16_to_cpu(ref->ServerType); |
3988 | node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); | 3997 | node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); |
3989 | 3998 | ||
@@ -4116,7 +4125,8 @@ getDFSRetry: | |||
4116 | 4125 | ||
4117 | /* parse returned result into more usable form */ | 4126 | /* parse returned result into more usable form */ |
4118 | rc = parse_DFS_referrals(pSMBr, num_of_nodes, | 4127 | rc = parse_DFS_referrals(pSMBr, num_of_nodes, |
4119 | target_nodes, nls_codepage); | 4128 | target_nodes, nls_codepage, remap, |
4129 | searchName); | ||
4120 | 4130 | ||
4121 | GetDFSRefExit: | 4131 | GetDFSRefExit: |
4122 | cifs_buf_release(pSMB); | 4132 | cifs_buf_release(pSMB); |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e5dca9a48d9f..683dee4d2f76 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -124,7 +124,7 @@ static int | |||
124 | cifs_reconnect(struct TCP_Server_Info *server) | 124 | cifs_reconnect(struct TCP_Server_Info *server) |
125 | { | 125 | { |
126 | int rc = 0; | 126 | int rc = 0; |
127 | struct list_head *tmp; | 127 | struct list_head *tmp, *tmp2; |
128 | struct cifsSesInfo *ses; | 128 | struct cifsSesInfo *ses; |
129 | struct cifsTconInfo *tcon; | 129 | struct cifsTconInfo *tcon; |
130 | struct mid_q_entry *mid_entry; | 130 | struct mid_q_entry *mid_entry; |
@@ -144,23 +144,17 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
144 | 144 | ||
145 | /* before reconnecting the tcp session, mark the smb session (uid) | 145 | /* before reconnecting the tcp session, mark the smb session (uid) |
146 | and the tid bad so they are not used until reconnected */ | 146 | and the tid bad so they are not used until reconnected */ |
147 | read_lock(&GlobalSMBSeslock); | 147 | read_lock(&cifs_tcp_ses_lock); |
148 | list_for_each(tmp, &GlobalSMBSessionList) { | 148 | list_for_each(tmp, &server->smb_ses_list) { |
149 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); | 149 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
150 | if (ses->server) { | 150 | ses->need_reconnect = true; |
151 | if (ses->server == server) { | 151 | ses->ipc_tid = 0; |
152 | ses->status = CifsNeedReconnect; | 152 | list_for_each(tmp2, &ses->tcon_list) { |
153 | ses->ipc_tid = 0; | 153 | tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list); |
154 | } | 154 | tcon->need_reconnect = true; |
155 | } | 155 | } |
156 | /* else tcp and smb sessions need reconnection */ | ||
157 | } | ||
158 | list_for_each(tmp, &GlobalTreeConnectionList) { | ||
159 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | ||
160 | if ((tcon->ses) && (tcon->ses->server == server)) | ||
161 | tcon->tidStatus = CifsNeedReconnect; | ||
162 | } | 156 | } |
163 | read_unlock(&GlobalSMBSeslock); | 157 | read_unlock(&cifs_tcp_ses_lock); |
164 | /* do not want to be sending data on a socket we are freeing */ | 158 | /* do not want to be sending data on a socket we are freeing */ |
165 | down(&server->tcpSem); | 159 | down(&server->tcpSem); |
166 | if (server->ssocket) { | 160 | if (server->ssocket) { |
@@ -193,7 +187,7 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
193 | while ((server->tcpStatus != CifsExiting) && | 187 | while ((server->tcpStatus != CifsExiting) && |
194 | (server->tcpStatus != CifsGood)) { | 188 | (server->tcpStatus != CifsGood)) { |
195 | try_to_freeze(); | 189 | try_to_freeze(); |
196 | if (server->protocolType == IPV6) { | 190 | if (server->addr.sockAddr6.sin6_family == AF_INET6) { |
197 | rc = ipv6_connect(&server->addr.sockAddr6, | 191 | rc = ipv6_connect(&server->addr.sockAddr6, |
198 | &server->ssocket, server->noautotune); | 192 | &server->ssocket, server->noautotune); |
199 | } else { | 193 | } else { |
@@ -417,9 +411,14 @@ incomplete_rcv: | |||
417 | msleep(1); /* minimum sleep to prevent looping | 411 | msleep(1); /* minimum sleep to prevent looping |
418 | allowing socket to clear and app threads to set | 412 | allowing socket to clear and app threads to set |
419 | tcpStatus CifsNeedReconnect if server hung */ | 413 | tcpStatus CifsNeedReconnect if server hung */ |
420 | if (pdu_length < 4) | 414 | if (pdu_length < 4) { |
415 | iov.iov_base = (4 - pdu_length) + | ||
416 | (char *)smb_buffer; | ||
417 | iov.iov_len = pdu_length; | ||
418 | smb_msg.msg_control = NULL; | ||
419 | smb_msg.msg_controllen = 0; | ||
421 | goto incomplete_rcv; | 420 | goto incomplete_rcv; |
422 | else | 421 | } else |
423 | continue; | 422 | continue; |
424 | } else if (length <= 0) { | 423 | } else if (length <= 0) { |
425 | if (server->tcpStatus == CifsNew) { | 424 | if (server->tcpStatus == CifsNew) { |
@@ -654,6 +653,11 @@ multi_t2_fnd: | |||
654 | } | 653 | } |
655 | } /* end while !EXITING */ | 654 | } /* end while !EXITING */ |
656 | 655 | ||
656 | /* take it off the list, if it's not already */ | ||
657 | write_lock(&cifs_tcp_ses_lock); | ||
658 | list_del_init(&server->tcp_ses_list); | ||
659 | write_unlock(&cifs_tcp_ses_lock); | ||
660 | |||
657 | spin_lock(&GlobalMid_Lock); | 661 | spin_lock(&GlobalMid_Lock); |
658 | server->tcpStatus = CifsExiting; | 662 | server->tcpStatus = CifsExiting; |
659 | spin_unlock(&GlobalMid_Lock); | 663 | spin_unlock(&GlobalMid_Lock); |
@@ -686,29 +690,29 @@ multi_t2_fnd: | |||
686 | if (smallbuf) /* no sense logging a debug message if NULL */ | 690 | if (smallbuf) /* no sense logging a debug message if NULL */ |
687 | cifs_small_buf_release(smallbuf); | 691 | cifs_small_buf_release(smallbuf); |
688 | 692 | ||
689 | read_lock(&GlobalSMBSeslock); | 693 | /* |
694 | * BB: we shouldn't have to do any of this. It shouldn't be | ||
695 | * possible to exit from the thread with active SMB sessions | ||
696 | */ | ||
697 | read_lock(&cifs_tcp_ses_lock); | ||
690 | if (list_empty(&server->pending_mid_q)) { | 698 | if (list_empty(&server->pending_mid_q)) { |
691 | /* loop through server session structures attached to this and | 699 | /* loop through server session structures attached to this and |
692 | mark them dead */ | 700 | mark them dead */ |
693 | list_for_each(tmp, &GlobalSMBSessionList) { | 701 | list_for_each(tmp, &server->smb_ses_list) { |
694 | ses = | 702 | ses = list_entry(tmp, struct cifsSesInfo, |
695 | list_entry(tmp, struct cifsSesInfo, | 703 | smb_ses_list); |
696 | cifsSessionList); | 704 | ses->status = CifsExiting; |
697 | if (ses->server == server) { | 705 | ses->server = NULL; |
698 | ses->status = CifsExiting; | ||
699 | ses->server = NULL; | ||
700 | } | ||
701 | } | 706 | } |
702 | read_unlock(&GlobalSMBSeslock); | 707 | read_unlock(&cifs_tcp_ses_lock); |
703 | } else { | 708 | } else { |
704 | /* although we can not zero the server struct pointer yet, | 709 | /* although we can not zero the server struct pointer yet, |
705 | since there are active requests which may depnd on them, | 710 | since there are active requests which may depnd on them, |
706 | mark the corresponding SMB sessions as exiting too */ | 711 | mark the corresponding SMB sessions as exiting too */ |
707 | list_for_each(tmp, &GlobalSMBSessionList) { | 712 | list_for_each(tmp, &server->smb_ses_list) { |
708 | ses = list_entry(tmp, struct cifsSesInfo, | 713 | ses = list_entry(tmp, struct cifsSesInfo, |
709 | cifsSessionList); | 714 | smb_ses_list); |
710 | if (ses->server == server) | 715 | ses->status = CifsExiting; |
711 | ses->status = CifsExiting; | ||
712 | } | 716 | } |
713 | 717 | ||
714 | spin_lock(&GlobalMid_Lock); | 718 | spin_lock(&GlobalMid_Lock); |
@@ -723,7 +727,7 @@ multi_t2_fnd: | |||
723 | } | 727 | } |
724 | } | 728 | } |
725 | spin_unlock(&GlobalMid_Lock); | 729 | spin_unlock(&GlobalMid_Lock); |
726 | read_unlock(&GlobalSMBSeslock); | 730 | read_unlock(&cifs_tcp_ses_lock); |
727 | /* 1/8th of sec is more than enough time for them to exit */ | 731 | /* 1/8th of sec is more than enough time for them to exit */ |
728 | msleep(125); | 732 | msleep(125); |
729 | } | 733 | } |
@@ -745,14 +749,13 @@ multi_t2_fnd: | |||
745 | if there are any pointing to this (e.g | 749 | if there are any pointing to this (e.g |
746 | if a crazy root user tried to kill cifsd | 750 | if a crazy root user tried to kill cifsd |
747 | kernel thread explicitly this might happen) */ | 751 | kernel thread explicitly this might happen) */ |
748 | write_lock(&GlobalSMBSeslock); | 752 | /* BB: This shouldn't be necessary, see above */ |
749 | list_for_each(tmp, &GlobalSMBSessionList) { | 753 | read_lock(&cifs_tcp_ses_lock); |
750 | ses = list_entry(tmp, struct cifsSesInfo, | 754 | list_for_each(tmp, &server->smb_ses_list) { |
751 | cifsSessionList); | 755 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
752 | if (ses->server == server) | 756 | ses->server = NULL; |
753 | ses->server = NULL; | ||
754 | } | 757 | } |
755 | write_unlock(&GlobalSMBSeslock); | 758 | read_unlock(&cifs_tcp_ses_lock); |
756 | 759 | ||
757 | kfree(server->hostname); | 760 | kfree(server->hostname); |
758 | task_to_wake = xchg(&server->tsk, NULL); | 761 | task_to_wake = xchg(&server->tsk, NULL); |
@@ -1352,94 +1355,158 @@ cifs_parse_mount_options(char *options, const char *devname, | |||
1352 | return 0; | 1355 | return 0; |
1353 | } | 1356 | } |
1354 | 1357 | ||
1355 | static struct cifsSesInfo * | 1358 | static struct TCP_Server_Info * |
1356 | cifs_find_tcp_session(struct in_addr *target_ip_addr, | 1359 | cifs_find_tcp_session(struct sockaddr *addr) |
1357 | struct in6_addr *target_ip6_addr, | ||
1358 | char *userName, struct TCP_Server_Info **psrvTcp) | ||
1359 | { | 1360 | { |
1360 | struct list_head *tmp; | 1361 | struct list_head *tmp; |
1361 | struct cifsSesInfo *ses; | 1362 | struct TCP_Server_Info *server; |
1362 | 1363 | struct sockaddr_in *addr4 = (struct sockaddr_in *) addr; | |
1363 | *psrvTcp = NULL; | 1364 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr; |
1365 | |||
1366 | write_lock(&cifs_tcp_ses_lock); | ||
1367 | list_for_each(tmp, &cifs_tcp_ses_list) { | ||
1368 | server = list_entry(tmp, struct TCP_Server_Info, | ||
1369 | tcp_ses_list); | ||
1370 | /* | ||
1371 | * the demux thread can exit on its own while still in CifsNew | ||
1372 | * so don't accept any sockets in that state. Since the | ||
1373 | * tcpStatus never changes back to CifsNew it's safe to check | ||
1374 | * for this without a lock. | ||
1375 | */ | ||
1376 | if (server->tcpStatus == CifsNew) | ||
1377 | continue; | ||
1364 | 1378 | ||
1365 | read_lock(&GlobalSMBSeslock); | 1379 | if (addr->sa_family == AF_INET && |
1366 | list_for_each(tmp, &GlobalSMBSessionList) { | 1380 | (addr4->sin_addr.s_addr != |
1367 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); | 1381 | server->addr.sockAddr.sin_addr.s_addr)) |
1368 | if (!ses->server) | 1382 | continue; |
1383 | else if (addr->sa_family == AF_INET6 && | ||
1384 | memcmp(&server->addr.sockAddr6.sin6_addr, | ||
1385 | &addr6->sin6_addr, sizeof(addr6->sin6_addr))) | ||
1369 | continue; | 1386 | continue; |
1370 | 1387 | ||
1371 | if (target_ip_addr && | 1388 | ++server->srv_count; |
1372 | ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr) | 1389 | write_unlock(&cifs_tcp_ses_lock); |
1373 | continue; | 1390 | cFYI(1, ("Existing tcp session with server found")); |
1374 | else if (target_ip6_addr && | 1391 | return server; |
1375 | memcmp(&ses->server->addr.sockAddr6.sin6_addr, | 1392 | } |
1376 | target_ip6_addr, sizeof(*target_ip6_addr))) | 1393 | write_unlock(&cifs_tcp_ses_lock); |
1377 | continue; | 1394 | return NULL; |
1378 | /* BB lock server and tcp session; increment use count here?? */ | 1395 | } |
1379 | 1396 | ||
1380 | /* found a match on the TCP session */ | 1397 | static void |
1381 | *psrvTcp = ses->server; | 1398 | cifs_put_tcp_session(struct TCP_Server_Info *server) |
1399 | { | ||
1400 | struct task_struct *task; | ||
1382 | 1401 | ||
1383 | /* BB check if reconnection needed */ | 1402 | write_lock(&cifs_tcp_ses_lock); |
1384 | if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) { | 1403 | if (--server->srv_count > 0) { |
1385 | read_unlock(&GlobalSMBSeslock); | 1404 | write_unlock(&cifs_tcp_ses_lock); |
1386 | /* Found exact match on both TCP and | 1405 | return; |
1387 | SMB sessions */ | ||
1388 | return ses; | ||
1389 | } | ||
1390 | /* else tcp and smb sessions need reconnection */ | ||
1391 | } | 1406 | } |
1392 | read_unlock(&GlobalSMBSeslock); | ||
1393 | 1407 | ||
1394 | return NULL; | 1408 | list_del_init(&server->tcp_ses_list); |
1409 | write_unlock(&cifs_tcp_ses_lock); | ||
1410 | |||
1411 | spin_lock(&GlobalMid_Lock); | ||
1412 | server->tcpStatus = CifsExiting; | ||
1413 | spin_unlock(&GlobalMid_Lock); | ||
1414 | |||
1415 | task = xchg(&server->tsk, NULL); | ||
1416 | if (task) | ||
1417 | force_sig(SIGKILL, task); | ||
1395 | } | 1418 | } |
1396 | 1419 | ||
1397 | static struct cifsTconInfo * | 1420 | static struct cifsSesInfo * |
1398 | find_unc(__be32 new_target_ip_addr, char *uncName, char *userName) | 1421 | cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) |
1399 | { | 1422 | { |
1400 | struct list_head *tmp; | 1423 | struct list_head *tmp; |
1401 | struct cifsTconInfo *tcon; | 1424 | struct cifsSesInfo *ses; |
1402 | __be32 old_ip; | ||
1403 | |||
1404 | read_lock(&GlobalSMBSeslock); | ||
1405 | 1425 | ||
1406 | list_for_each(tmp, &GlobalTreeConnectionList) { | 1426 | write_lock(&cifs_tcp_ses_lock); |
1407 | cFYI(1, ("Next tcon")); | 1427 | list_for_each(tmp, &server->smb_ses_list) { |
1408 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | 1428 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
1409 | if (!tcon->ses || !tcon->ses->server) | 1429 | if (strncmp(ses->userName, username, MAX_USERNAME_SIZE)) |
1410 | continue; | 1430 | continue; |
1411 | 1431 | ||
1412 | old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr; | 1432 | ++ses->ses_count; |
1413 | cFYI(1, ("old ip addr: %x == new ip %x ?", | 1433 | write_unlock(&cifs_tcp_ses_lock); |
1414 | old_ip, new_target_ip_addr)); | 1434 | return ses; |
1435 | } | ||
1436 | write_unlock(&cifs_tcp_ses_lock); | ||
1437 | return NULL; | ||
1438 | } | ||
1415 | 1439 | ||
1416 | if (old_ip != new_target_ip_addr) | 1440 | static void |
1417 | continue; | 1441 | cifs_put_smb_ses(struct cifsSesInfo *ses) |
1442 | { | ||
1443 | int xid; | ||
1444 | struct TCP_Server_Info *server = ses->server; | ||
1418 | 1445 | ||
1419 | /* BB lock tcon, server, tcp session and increment use count? */ | 1446 | write_lock(&cifs_tcp_ses_lock); |
1420 | /* found a match on the TCP session */ | 1447 | if (--ses->ses_count > 0) { |
1421 | /* BB check if reconnection needed */ | 1448 | write_unlock(&cifs_tcp_ses_lock); |
1422 | cFYI(1, ("IP match, old UNC: %s new: %s", | 1449 | return; |
1423 | tcon->treeName, uncName)); | 1450 | } |
1424 | 1451 | ||
1425 | if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE)) | 1452 | list_del_init(&ses->smb_ses_list); |
1426 | continue; | 1453 | write_unlock(&cifs_tcp_ses_lock); |
1427 | 1454 | ||
1428 | cFYI(1, ("and old usr: %s new: %s", | 1455 | if (ses->status == CifsGood) { |
1429 | tcon->treeName, uncName)); | 1456 | xid = GetXid(); |
1457 | CIFSSMBLogoff(xid, ses); | ||
1458 | _FreeXid(xid); | ||
1459 | } | ||
1460 | sesInfoFree(ses); | ||
1461 | cifs_put_tcp_session(server); | ||
1462 | } | ||
1430 | 1463 | ||
1431 | if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE)) | 1464 | static struct cifsTconInfo * |
1465 | cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) | ||
1466 | { | ||
1467 | struct list_head *tmp; | ||
1468 | struct cifsTconInfo *tcon; | ||
1469 | |||
1470 | write_lock(&cifs_tcp_ses_lock); | ||
1471 | list_for_each(tmp, &ses->tcon_list) { | ||
1472 | tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); | ||
1473 | if (tcon->tidStatus == CifsExiting) | ||
1474 | continue; | ||
1475 | if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE)) | ||
1432 | continue; | 1476 | continue; |
1433 | 1477 | ||
1434 | /* matched smb session (user name) */ | 1478 | ++tcon->tc_count; |
1435 | read_unlock(&GlobalSMBSeslock); | 1479 | write_unlock(&cifs_tcp_ses_lock); |
1436 | return tcon; | 1480 | return tcon; |
1437 | } | 1481 | } |
1438 | 1482 | write_unlock(&cifs_tcp_ses_lock); | |
1439 | read_unlock(&GlobalSMBSeslock); | ||
1440 | return NULL; | 1483 | return NULL; |
1441 | } | 1484 | } |
1442 | 1485 | ||
1486 | static void | ||
1487 | cifs_put_tcon(struct cifsTconInfo *tcon) | ||
1488 | { | ||
1489 | int xid; | ||
1490 | struct cifsSesInfo *ses = tcon->ses; | ||
1491 | |||
1492 | write_lock(&cifs_tcp_ses_lock); | ||
1493 | if (--tcon->tc_count > 0) { | ||
1494 | write_unlock(&cifs_tcp_ses_lock); | ||
1495 | return; | ||
1496 | } | ||
1497 | |||
1498 | list_del_init(&tcon->tcon_list); | ||
1499 | write_unlock(&cifs_tcp_ses_lock); | ||
1500 | |||
1501 | xid = GetXid(); | ||
1502 | CIFSSMBTDis(xid, tcon); | ||
1503 | _FreeXid(xid); | ||
1504 | |||
1505 | DeleteTconOplockQEntries(tcon); | ||
1506 | tconInfoFree(tcon); | ||
1507 | cifs_put_smb_ses(ses); | ||
1508 | } | ||
1509 | |||
1443 | int | 1510 | int |
1444 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, | 1511 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, |
1445 | const struct nls_table *nls_codepage, unsigned int *pnum_referrals, | 1512 | const struct nls_table *nls_codepage, unsigned int *pnum_referrals, |
@@ -1876,14 +1943,90 @@ convert_delimiter(char *path, char delim) | |||
1876 | } | 1943 | } |
1877 | } | 1944 | } |
1878 | 1945 | ||
1879 | static void | 1946 | static void setup_cifs_sb(struct smb_vol *pvolume_info, |
1880 | kill_cifsd(struct TCP_Server_Info *server) | 1947 | struct cifs_sb_info *cifs_sb) |
1881 | { | 1948 | { |
1882 | struct task_struct *task; | 1949 | if (pvolume_info->rsize > CIFSMaxBufSize) { |
1883 | 1950 | cERROR(1, ("rsize %d too large, using MaxBufSize", | |
1884 | task = xchg(&server->tsk, NULL); | 1951 | pvolume_info->rsize)); |
1885 | if (task) | 1952 | cifs_sb->rsize = CIFSMaxBufSize; |
1886 | force_sig(SIGKILL, task); | 1953 | } else if ((pvolume_info->rsize) && |
1954 | (pvolume_info->rsize <= CIFSMaxBufSize)) | ||
1955 | cifs_sb->rsize = pvolume_info->rsize; | ||
1956 | else /* default */ | ||
1957 | cifs_sb->rsize = CIFSMaxBufSize; | ||
1958 | |||
1959 | if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { | ||
1960 | cERROR(1, ("wsize %d too large, using 4096 instead", | ||
1961 | pvolume_info->wsize)); | ||
1962 | cifs_sb->wsize = 4096; | ||
1963 | } else if (pvolume_info->wsize) | ||
1964 | cifs_sb->wsize = pvolume_info->wsize; | ||
1965 | else | ||
1966 | cifs_sb->wsize = min_t(const int, | ||
1967 | PAGEVEC_SIZE * PAGE_CACHE_SIZE, | ||
1968 | 127*1024); | ||
1969 | /* old default of CIFSMaxBufSize was too small now | ||
1970 | that SMB Write2 can send multiple pages in kvec. | ||
1971 | RFC1001 does not describe what happens when frame | ||
1972 | bigger than 128K is sent so use that as max in | ||
1973 | conjunction with 52K kvec constraint on arch with 4K | ||
1974 | page size */ | ||
1975 | |||
1976 | if (cifs_sb->rsize < 2048) { | ||
1977 | cifs_sb->rsize = 2048; | ||
1978 | /* Windows ME may prefer this */ | ||
1979 | cFYI(1, ("readsize set to minimum: 2048")); | ||
1980 | } | ||
1981 | /* calculate prepath */ | ||
1982 | cifs_sb->prepath = pvolume_info->prepath; | ||
1983 | if (cifs_sb->prepath) { | ||
1984 | cifs_sb->prepathlen = strlen(cifs_sb->prepath); | ||
1985 | /* we can not convert the / to \ in the path | ||
1986 | separators in the prefixpath yet because we do not | ||
1987 | know (until reset_cifs_unix_caps is called later) | ||
1988 | whether POSIX PATH CAP is available. We normalize | ||
1989 | the / to \ after reset_cifs_unix_caps is called */ | ||
1990 | pvolume_info->prepath = NULL; | ||
1991 | } else | ||
1992 | cifs_sb->prepathlen = 0; | ||
1993 | cifs_sb->mnt_uid = pvolume_info->linux_uid; | ||
1994 | cifs_sb->mnt_gid = pvolume_info->linux_gid; | ||
1995 | cifs_sb->mnt_file_mode = pvolume_info->file_mode; | ||
1996 | cifs_sb->mnt_dir_mode = pvolume_info->dir_mode; | ||
1997 | cFYI(1, ("file mode: 0x%x dir mode: 0x%x", | ||
1998 | cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); | ||
1999 | |||
2000 | if (pvolume_info->noperm) | ||
2001 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; | ||
2002 | if (pvolume_info->setuids) | ||
2003 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; | ||
2004 | if (pvolume_info->server_ino) | ||
2005 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; | ||
2006 | if (pvolume_info->remap) | ||
2007 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; | ||
2008 | if (pvolume_info->no_xattr) | ||
2009 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; | ||
2010 | if (pvolume_info->sfu_emul) | ||
2011 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; | ||
2012 | if (pvolume_info->nobrl) | ||
2013 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; | ||
2014 | if (pvolume_info->cifs_acl) | ||
2015 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; | ||
2016 | if (pvolume_info->override_uid) | ||
2017 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; | ||
2018 | if (pvolume_info->override_gid) | ||
2019 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; | ||
2020 | if (pvolume_info->dynperm) | ||
2021 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; | ||
2022 | if (pvolume_info->direct_io) { | ||
2023 | cFYI(1, ("mounting share using direct i/o")); | ||
2024 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; | ||
2025 | } | ||
2026 | |||
2027 | if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) | ||
2028 | cERROR(1, ("mount option dynperm ignored if cifsacl " | ||
2029 | "mount option supported")); | ||
1887 | } | 2030 | } |
1888 | 2031 | ||
1889 | int | 2032 | int |
@@ -1892,13 +2035,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1892 | { | 2035 | { |
1893 | int rc = 0; | 2036 | int rc = 0; |
1894 | int xid; | 2037 | int xid; |
1895 | int address_type = AF_INET; | ||
1896 | struct socket *csocket = NULL; | 2038 | struct socket *csocket = NULL; |
1897 | struct sockaddr_in sin_server; | 2039 | struct sockaddr addr; |
1898 | struct sockaddr_in6 sin_server6; | 2040 | struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr; |
2041 | struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; | ||
1899 | struct smb_vol volume_info; | 2042 | struct smb_vol volume_info; |
1900 | struct cifsSesInfo *pSesInfo = NULL; | 2043 | struct cifsSesInfo *pSesInfo = NULL; |
1901 | struct cifsSesInfo *existingCifsSes = NULL; | ||
1902 | struct cifsTconInfo *tcon = NULL; | 2044 | struct cifsTconInfo *tcon = NULL; |
1903 | struct TCP_Server_Info *srvTcp = NULL; | 2045 | struct TCP_Server_Info *srvTcp = NULL; |
1904 | 2046 | ||
@@ -1906,6 +2048,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1906 | 2048 | ||
1907 | /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ | 2049 | /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ |
1908 | 2050 | ||
2051 | memset(&addr, 0, sizeof(struct sockaddr)); | ||
1909 | memset(&volume_info, 0, sizeof(struct smb_vol)); | 2052 | memset(&volume_info, 0, sizeof(struct smb_vol)); |
1910 | if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { | 2053 | if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { |
1911 | rc = -EINVAL; | 2054 | rc = -EINVAL; |
@@ -1928,16 +2071,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1928 | 2071 | ||
1929 | if (volume_info.UNCip && volume_info.UNC) { | 2072 | if (volume_info.UNCip && volume_info.UNC) { |
1930 | rc = cifs_inet_pton(AF_INET, volume_info.UNCip, | 2073 | rc = cifs_inet_pton(AF_INET, volume_info.UNCip, |
1931 | &sin_server.sin_addr.s_addr); | 2074 | &sin_server->sin_addr.s_addr); |
1932 | 2075 | ||
1933 | if (rc <= 0) { | 2076 | if (rc <= 0) { |
1934 | /* not ipv4 address, try ipv6 */ | 2077 | /* not ipv4 address, try ipv6 */ |
1935 | rc = cifs_inet_pton(AF_INET6, volume_info.UNCip, | 2078 | rc = cifs_inet_pton(AF_INET6, volume_info.UNCip, |
1936 | &sin_server6.sin6_addr.in6_u); | 2079 | &sin_server6->sin6_addr.in6_u); |
1937 | if (rc > 0) | 2080 | if (rc > 0) |
1938 | address_type = AF_INET6; | 2081 | addr.sa_family = AF_INET6; |
1939 | } else { | 2082 | } else { |
1940 | address_type = AF_INET; | 2083 | addr.sa_family = AF_INET; |
1941 | } | 2084 | } |
1942 | 2085 | ||
1943 | if (rc <= 0) { | 2086 | if (rc <= 0) { |
@@ -1977,41 +2120,25 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1977 | } | 2120 | } |
1978 | } | 2121 | } |
1979 | 2122 | ||
1980 | if (address_type == AF_INET) | 2123 | srvTcp = cifs_find_tcp_session(&addr); |
1981 | existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr, | 2124 | if (!srvTcp) { /* create socket */ |
1982 | NULL /* no ipv6 addr */, | 2125 | if (addr.sa_family == AF_INET6) { |
1983 | volume_info.username, &srvTcp); | ||
1984 | else if (address_type == AF_INET6) { | ||
1985 | cFYI(1, ("looking for ipv6 address")); | ||
1986 | existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */, | ||
1987 | &sin_server6.sin6_addr, | ||
1988 | volume_info.username, &srvTcp); | ||
1989 | } else { | ||
1990 | rc = -EINVAL; | ||
1991 | goto out; | ||
1992 | } | ||
1993 | |||
1994 | if (srvTcp) { | ||
1995 | cFYI(1, ("Existing tcp session with server found")); | ||
1996 | } else { /* create socket */ | ||
1997 | if (volume_info.port) | ||
1998 | sin_server.sin_port = htons(volume_info.port); | ||
1999 | else | ||
2000 | sin_server.sin_port = 0; | ||
2001 | if (address_type == AF_INET6) { | ||
2002 | cFYI(1, ("attempting ipv6 connect")); | 2126 | cFYI(1, ("attempting ipv6 connect")); |
2003 | /* BB should we allow ipv6 on port 139? */ | 2127 | /* BB should we allow ipv6 on port 139? */ |
2004 | /* other OS never observed in Wild doing 139 with v6 */ | 2128 | /* other OS never observed in Wild doing 139 with v6 */ |
2005 | rc = ipv6_connect(&sin_server6, &csocket, | 2129 | sin_server6->sin6_port = htons(volume_info.port); |
2130 | rc = ipv6_connect(sin_server6, &csocket, | ||
2006 | volume_info.noblocksnd); | 2131 | volume_info.noblocksnd); |
2007 | } else | 2132 | } else { |
2008 | rc = ipv4_connect(&sin_server, &csocket, | 2133 | sin_server->sin_port = htons(volume_info.port); |
2134 | rc = ipv4_connect(sin_server, &csocket, | ||
2009 | volume_info.source_rfc1001_name, | 2135 | volume_info.source_rfc1001_name, |
2010 | volume_info.target_rfc1001_name, | 2136 | volume_info.target_rfc1001_name, |
2011 | volume_info.noblocksnd, | 2137 | volume_info.noblocksnd, |
2012 | volume_info.noautotune); | 2138 | volume_info.noautotune); |
2139 | } | ||
2013 | if (rc < 0) { | 2140 | if (rc < 0) { |
2014 | cERROR(1, ("Error connecting to IPv4 socket. " | 2141 | cERROR(1, ("Error connecting to socket. " |
2015 | "Aborting operation")); | 2142 | "Aborting operation")); |
2016 | if (csocket != NULL) | 2143 | if (csocket != NULL) |
2017 | sock_release(csocket); | 2144 | sock_release(csocket); |
@@ -2026,12 +2153,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2026 | } else { | 2153 | } else { |
2027 | srvTcp->noblocksnd = volume_info.noblocksnd; | 2154 | srvTcp->noblocksnd = volume_info.noblocksnd; |
2028 | srvTcp->noautotune = volume_info.noautotune; | 2155 | srvTcp->noautotune = volume_info.noautotune; |
2029 | memcpy(&srvTcp->addr.sockAddr, &sin_server, | 2156 | if (addr.sa_family == AF_INET6) |
2030 | sizeof(struct sockaddr_in)); | 2157 | memcpy(&srvTcp->addr.sockAddr6, sin_server6, |
2158 | sizeof(struct sockaddr_in6)); | ||
2159 | else | ||
2160 | memcpy(&srvTcp->addr.sockAddr, sin_server, | ||
2161 | sizeof(struct sockaddr_in)); | ||
2031 | atomic_set(&srvTcp->inFlight, 0); | 2162 | atomic_set(&srvTcp->inFlight, 0); |
2032 | /* BB Add code for ipv6 case too */ | 2163 | /* BB Add code for ipv6 case too */ |
2033 | srvTcp->ssocket = csocket; | 2164 | srvTcp->ssocket = csocket; |
2034 | srvTcp->protocolType = IPV4; | ||
2035 | srvTcp->hostname = extract_hostname(volume_info.UNC); | 2165 | srvTcp->hostname = extract_hostname(volume_info.UNC); |
2036 | if (IS_ERR(srvTcp->hostname)) { | 2166 | if (IS_ERR(srvTcp->hostname)) { |
2037 | rc = PTR_ERR(srvTcp->hostname); | 2167 | rc = PTR_ERR(srvTcp->hostname); |
@@ -2061,15 +2191,28 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2061 | memcpy(srvTcp->server_RFC1001_name, | 2191 | memcpy(srvTcp->server_RFC1001_name, |
2062 | volume_info.target_rfc1001_name, 16); | 2192 | volume_info.target_rfc1001_name, 16); |
2063 | srvTcp->sequence_number = 0; | 2193 | srvTcp->sequence_number = 0; |
2194 | INIT_LIST_HEAD(&srvTcp->tcp_ses_list); | ||
2195 | INIT_LIST_HEAD(&srvTcp->smb_ses_list); | ||
2196 | ++srvTcp->srv_count; | ||
2197 | write_lock(&cifs_tcp_ses_lock); | ||
2198 | list_add(&srvTcp->tcp_ses_list, | ||
2199 | &cifs_tcp_ses_list); | ||
2200 | write_unlock(&cifs_tcp_ses_lock); | ||
2064 | } | 2201 | } |
2065 | } | 2202 | } |
2066 | 2203 | ||
2067 | if (existingCifsSes) { | 2204 | pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username); |
2068 | pSesInfo = existingCifsSes; | 2205 | if (pSesInfo) { |
2069 | cFYI(1, ("Existing smb sess found (status=%d)", | 2206 | cFYI(1, ("Existing smb sess found (status=%d)", |
2070 | pSesInfo->status)); | 2207 | pSesInfo->status)); |
2208 | /* | ||
2209 | * The existing SMB session already has a reference to srvTcp, | ||
2210 | * so we can put back the extra one we got before | ||
2211 | */ | ||
2212 | cifs_put_tcp_session(srvTcp); | ||
2213 | |||
2071 | down(&pSesInfo->sesSem); | 2214 | down(&pSesInfo->sesSem); |
2072 | if (pSesInfo->status == CifsNeedReconnect) { | 2215 | if (pSesInfo->need_reconnect) { |
2073 | cFYI(1, ("Session needs reconnect")); | 2216 | cFYI(1, ("Session needs reconnect")); |
2074 | rc = cifs_setup_session(xid, pSesInfo, | 2217 | rc = cifs_setup_session(xid, pSesInfo, |
2075 | cifs_sb->local_nls); | 2218 | cifs_sb->local_nls); |
@@ -2078,187 +2221,101 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2078 | } else if (!rc) { | 2221 | } else if (!rc) { |
2079 | cFYI(1, ("Existing smb sess not found")); | 2222 | cFYI(1, ("Existing smb sess not found")); |
2080 | pSesInfo = sesInfoAlloc(); | 2223 | pSesInfo = sesInfoAlloc(); |
2081 | if (pSesInfo == NULL) | 2224 | if (pSesInfo == NULL) { |
2082 | rc = -ENOMEM; | 2225 | rc = -ENOMEM; |
2083 | else { | 2226 | goto mount_fail_check; |
2084 | pSesInfo->server = srvTcp; | 2227 | } |
2085 | sprintf(pSesInfo->serverName, "%u.%u.%u.%u", | 2228 | |
2086 | NIPQUAD(sin_server.sin_addr.s_addr)); | 2229 | /* new SMB session uses our srvTcp ref */ |
2087 | } | 2230 | pSesInfo->server = srvTcp; |
2231 | sprintf(pSesInfo->serverName, "%u.%u.%u.%u", | ||
2232 | NIPQUAD(sin_server->sin_addr.s_addr)); | ||
2233 | |||
2234 | write_lock(&cifs_tcp_ses_lock); | ||
2235 | list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list); | ||
2236 | write_unlock(&cifs_tcp_ses_lock); | ||
2237 | |||
2238 | /* volume_info.password freed at unmount */ | ||
2239 | if (volume_info.password) { | ||
2240 | pSesInfo->password = volume_info.password; | ||
2241 | /* set to NULL to prevent freeing on exit */ | ||
2242 | volume_info.password = NULL; | ||
2243 | } | ||
2244 | if (volume_info.username) | ||
2245 | strncpy(pSesInfo->userName, volume_info.username, | ||
2246 | MAX_USERNAME_SIZE); | ||
2247 | if (volume_info.domainname) { | ||
2248 | int len = strlen(volume_info.domainname); | ||
2249 | pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL); | ||
2250 | if (pSesInfo->domainName) | ||
2251 | strcpy(pSesInfo->domainName, | ||
2252 | volume_info.domainname); | ||
2253 | } | ||
2254 | pSesInfo->linux_uid = volume_info.linux_uid; | ||
2255 | pSesInfo->overrideSecFlg = volume_info.secFlg; | ||
2256 | down(&pSesInfo->sesSem); | ||
2088 | 2257 | ||
2089 | if (!rc) { | 2258 | /* BB FIXME need to pass vol->secFlgs BB */ |
2090 | /* volume_info.password freed at unmount */ | 2259 | rc = cifs_setup_session(xid, pSesInfo, |
2091 | if (volume_info.password) { | 2260 | cifs_sb->local_nls); |
2092 | pSesInfo->password = volume_info.password; | 2261 | up(&pSesInfo->sesSem); |
2093 | /* set to NULL to prevent freeing on exit */ | ||
2094 | volume_info.password = NULL; | ||
2095 | } | ||
2096 | if (volume_info.username) | ||
2097 | strncpy(pSesInfo->userName, | ||
2098 | volume_info.username, | ||
2099 | MAX_USERNAME_SIZE); | ||
2100 | if (volume_info.domainname) { | ||
2101 | int len = strlen(volume_info.domainname); | ||
2102 | pSesInfo->domainName = | ||
2103 | kmalloc(len + 1, GFP_KERNEL); | ||
2104 | if (pSesInfo->domainName) | ||
2105 | strcpy(pSesInfo->domainName, | ||
2106 | volume_info.domainname); | ||
2107 | } | ||
2108 | pSesInfo->linux_uid = volume_info.linux_uid; | ||
2109 | pSesInfo->overrideSecFlg = volume_info.secFlg; | ||
2110 | down(&pSesInfo->sesSem); | ||
2111 | /* BB FIXME need to pass vol->secFlgs BB */ | ||
2112 | rc = cifs_setup_session(xid, pSesInfo, | ||
2113 | cifs_sb->local_nls); | ||
2114 | up(&pSesInfo->sesSem); | ||
2115 | if (!rc) | ||
2116 | atomic_inc(&srvTcp->socketUseCount); | ||
2117 | } | ||
2118 | } | 2262 | } |
2119 | 2263 | ||
2120 | /* search for existing tcon to this server share */ | 2264 | /* search for existing tcon to this server share */ |
2121 | if (!rc) { | 2265 | if (!rc) { |
2122 | if (volume_info.rsize > CIFSMaxBufSize) { | 2266 | setup_cifs_sb(&volume_info, cifs_sb); |
2123 | cERROR(1, ("rsize %d too large, using MaxBufSize", | ||
2124 | volume_info.rsize)); | ||
2125 | cifs_sb->rsize = CIFSMaxBufSize; | ||
2126 | } else if ((volume_info.rsize) && | ||
2127 | (volume_info.rsize <= CIFSMaxBufSize)) | ||
2128 | cifs_sb->rsize = volume_info.rsize; | ||
2129 | else /* default */ | ||
2130 | cifs_sb->rsize = CIFSMaxBufSize; | ||
2131 | |||
2132 | if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { | ||
2133 | cERROR(1, ("wsize %d too large, using 4096 instead", | ||
2134 | volume_info.wsize)); | ||
2135 | cifs_sb->wsize = 4096; | ||
2136 | } else if (volume_info.wsize) | ||
2137 | cifs_sb->wsize = volume_info.wsize; | ||
2138 | else | ||
2139 | cifs_sb->wsize = | ||
2140 | min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE, | ||
2141 | 127*1024); | ||
2142 | /* old default of CIFSMaxBufSize was too small now | ||
2143 | that SMB Write2 can send multiple pages in kvec. | ||
2144 | RFC1001 does not describe what happens when frame | ||
2145 | bigger than 128K is sent so use that as max in | ||
2146 | conjunction with 52K kvec constraint on arch with 4K | ||
2147 | page size */ | ||
2148 | |||
2149 | if (cifs_sb->rsize < 2048) { | ||
2150 | cifs_sb->rsize = 2048; | ||
2151 | /* Windows ME may prefer this */ | ||
2152 | cFYI(1, ("readsize set to minimum: 2048")); | ||
2153 | } | ||
2154 | /* calculate prepath */ | ||
2155 | cifs_sb->prepath = volume_info.prepath; | ||
2156 | if (cifs_sb->prepath) { | ||
2157 | cifs_sb->prepathlen = strlen(cifs_sb->prepath); | ||
2158 | /* we can not convert the / to \ in the path | ||
2159 | separators in the prefixpath yet because we do not | ||
2160 | know (until reset_cifs_unix_caps is called later) | ||
2161 | whether POSIX PATH CAP is available. We normalize | ||
2162 | the / to \ after reset_cifs_unix_caps is called */ | ||
2163 | volume_info.prepath = NULL; | ||
2164 | } else | ||
2165 | cifs_sb->prepathlen = 0; | ||
2166 | cifs_sb->mnt_uid = volume_info.linux_uid; | ||
2167 | cifs_sb->mnt_gid = volume_info.linux_gid; | ||
2168 | cifs_sb->mnt_file_mode = volume_info.file_mode; | ||
2169 | cifs_sb->mnt_dir_mode = volume_info.dir_mode; | ||
2170 | cFYI(1, ("file mode: 0x%x dir mode: 0x%x", | ||
2171 | cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); | ||
2172 | |||
2173 | if (volume_info.noperm) | ||
2174 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; | ||
2175 | if (volume_info.setuids) | ||
2176 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; | ||
2177 | if (volume_info.server_ino) | ||
2178 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; | ||
2179 | if (volume_info.remap) | ||
2180 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; | ||
2181 | if (volume_info.no_xattr) | ||
2182 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; | ||
2183 | if (volume_info.sfu_emul) | ||
2184 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; | ||
2185 | if (volume_info.nobrl) | ||
2186 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; | ||
2187 | if (volume_info.cifs_acl) | ||
2188 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; | ||
2189 | if (volume_info.override_uid) | ||
2190 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; | ||
2191 | if (volume_info.override_gid) | ||
2192 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; | ||
2193 | if (volume_info.dynperm) | ||
2194 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; | ||
2195 | if (volume_info.direct_io) { | ||
2196 | cFYI(1, ("mounting share using direct i/o")); | ||
2197 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; | ||
2198 | } | ||
2199 | 2267 | ||
2200 | if ((volume_info.cifs_acl) && (volume_info.dynperm)) | 2268 | tcon = cifs_find_tcon(pSesInfo, volume_info.UNC); |
2201 | cERROR(1, ("mount option dynperm ignored if cifsacl " | ||
2202 | "mount option supported")); | ||
2203 | |||
2204 | tcon = | ||
2205 | find_unc(sin_server.sin_addr.s_addr, volume_info.UNC, | ||
2206 | volume_info.username); | ||
2207 | if (tcon) { | 2269 | if (tcon) { |
2208 | cFYI(1, ("Found match on UNC path")); | 2270 | cFYI(1, ("Found match on UNC path")); |
2209 | /* we can have only one retry value for a connection | 2271 | /* existing tcon already has a reference */ |
2210 | to a share so for resources mounted more than once | 2272 | cifs_put_smb_ses(pSesInfo); |
2211 | to the same server share the last value passed in | ||
2212 | for the retry flag is used */ | ||
2213 | tcon->retry = volume_info.retry; | ||
2214 | tcon->nocase = volume_info.nocase; | ||
2215 | tcon->local_lease = volume_info.local_lease; | ||
2216 | if (tcon->seal != volume_info.seal) | 2273 | if (tcon->seal != volume_info.seal) |
2217 | cERROR(1, ("transport encryption setting " | 2274 | cERROR(1, ("transport encryption setting " |
2218 | "conflicts with existing tid")); | 2275 | "conflicts with existing tid")); |
2219 | } else { | 2276 | } else { |
2220 | tcon = tconInfoAlloc(); | 2277 | tcon = tconInfoAlloc(); |
2221 | if (tcon == NULL) | 2278 | if (tcon == NULL) { |
2222 | rc = -ENOMEM; | 2279 | rc = -ENOMEM; |
2223 | else { | 2280 | goto mount_fail_check; |
2224 | /* check for null share name ie connecting to | 2281 | } |
2225 | * dfs root */ | 2282 | tcon->ses = pSesInfo; |
2226 | 2283 | ||
2227 | /* BB check if this works for exactly length | 2284 | /* check for null share name ie connect to dfs root */ |
2228 | * three strings */ | 2285 | if ((strchr(volume_info.UNC + 3, '\\') == NULL) |
2229 | if ((strchr(volume_info.UNC + 3, '\\') == NULL) | 2286 | && (strchr(volume_info.UNC + 3, '/') == NULL)) { |
2230 | && (strchr(volume_info.UNC + 3, '/') == | 2287 | /* rc = connect_to_dfs_path(...) */ |
2231 | NULL)) { | 2288 | cFYI(1, ("DFS root not supported")); |
2232 | /* rc = connect_to_dfs_path(xid, pSesInfo, | 2289 | rc = -ENODEV; |
2233 | "", cifs_sb->local_nls, | 2290 | goto mount_fail_check; |
2234 | cifs_sb->mnt_cifs_flags & | 2291 | } else { |
2235 | CIFS_MOUNT_MAP_SPECIAL_CHR);*/ | 2292 | /* BB Do we need to wrap sesSem around |
2236 | cFYI(1, ("DFS root not supported")); | 2293 | * this TCon call and Unix SetFS as |
2237 | rc = -ENODEV; | 2294 | * we do on SessSetup and reconnect? */ |
2238 | goto out; | 2295 | rc = CIFSTCon(xid, pSesInfo, volume_info.UNC, |
2239 | } else { | 2296 | tcon, cifs_sb->local_nls); |
2240 | /* BB Do we need to wrap sesSem around | 2297 | cFYI(1, ("CIFS Tcon rc = %d", rc)); |
2241 | * this TCon call and Unix SetFS as | 2298 | if (volume_info.nodfs) { |
2242 | * we do on SessSetup and reconnect? */ | 2299 | tcon->Flags &= ~SMB_SHARE_IS_IN_DFS; |
2243 | rc = CIFSTCon(xid, pSesInfo, | 2300 | cFYI(1, ("DFS disabled (%d)", |
2244 | volume_info.UNC, | 2301 | tcon->Flags)); |
2245 | tcon, cifs_sb->local_nls); | ||
2246 | cFYI(1, ("CIFS Tcon rc = %d", rc)); | ||
2247 | if (volume_info.nodfs) { | ||
2248 | tcon->Flags &= | ||
2249 | ~SMB_SHARE_IS_IN_DFS; | ||
2250 | cFYI(1, ("DFS disabled (%d)", | ||
2251 | tcon->Flags)); | ||
2252 | } | ||
2253 | } | ||
2254 | if (!rc) { | ||
2255 | atomic_inc(&pSesInfo->inUse); | ||
2256 | tcon->retry = volume_info.retry; | ||
2257 | tcon->nocase = volume_info.nocase; | ||
2258 | tcon->seal = volume_info.seal; | ||
2259 | } | 2302 | } |
2260 | } | 2303 | } |
2261 | } | 2304 | if (rc) |
2305 | goto mount_fail_check; | ||
2306 | tcon->seal = volume_info.seal; | ||
2307 | write_lock(&cifs_tcp_ses_lock); | ||
2308 | list_add(&tcon->tcon_list, &pSesInfo->tcon_list); | ||
2309 | write_unlock(&cifs_tcp_ses_lock); | ||
2310 | } | ||
2311 | |||
2312 | /* we can have only one retry value for a connection | ||
2313 | to a share so for resources mounted more than once | ||
2314 | to the same server share the last value passed in | ||
2315 | for the retry flag is used */ | ||
2316 | tcon->retry = volume_info.retry; | ||
2317 | tcon->nocase = volume_info.nocase; | ||
2318 | tcon->local_lease = volume_info.local_lease; | ||
2262 | } | 2319 | } |
2263 | if (pSesInfo) { | 2320 | if (pSesInfo) { |
2264 | if (pSesInfo->capabilities & CAP_LARGE_FILES) { | 2321 | if (pSesInfo->capabilities & CAP_LARGE_FILES) { |
@@ -2270,80 +2327,49 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2270 | /* BB FIXME fix time_gran to be larger for LANMAN sessions */ | 2327 | /* BB FIXME fix time_gran to be larger for LANMAN sessions */ |
2271 | sb->s_time_gran = 100; | 2328 | sb->s_time_gran = 100; |
2272 | 2329 | ||
2273 | /* on error free sesinfo and tcon struct if needed */ | 2330 | mount_fail_check: |
2331 | /* on error free sesinfo and tcon struct if needed */ | ||
2274 | if (rc) { | 2332 | if (rc) { |
2275 | /* if session setup failed, use count is zero but | 2333 | /* If find_unc succeeded then rc == 0 so we can not end */ |
2276 | we still need to free cifsd thread */ | 2334 | /* up accidently freeing someone elses tcon struct */ |
2277 | if (atomic_read(&srvTcp->socketUseCount) == 0) { | 2335 | if (tcon) |
2278 | spin_lock(&GlobalMid_Lock); | 2336 | cifs_put_tcon(tcon); |
2279 | srvTcp->tcpStatus = CifsExiting; | 2337 | else if (pSesInfo) |
2280 | spin_unlock(&GlobalMid_Lock); | 2338 | cifs_put_smb_ses(pSesInfo); |
2281 | kill_cifsd(srvTcp); | ||
2282 | } | ||
2283 | /* If find_unc succeeded then rc == 0 so we can not end */ | ||
2284 | if (tcon) /* up accidently freeing someone elses tcon struct */ | ||
2285 | tconInfoFree(tcon); | ||
2286 | if (existingCifsSes == NULL) { | ||
2287 | if (pSesInfo) { | ||
2288 | if ((pSesInfo->server) && | ||
2289 | (pSesInfo->status == CifsGood)) { | ||
2290 | int temp_rc; | ||
2291 | temp_rc = CIFSSMBLogoff(xid, pSesInfo); | ||
2292 | /* if the socketUseCount is now zero */ | ||
2293 | if ((temp_rc == -ESHUTDOWN) && | ||
2294 | (pSesInfo->server)) | ||
2295 | kill_cifsd(pSesInfo->server); | ||
2296 | } else { | ||
2297 | cFYI(1, ("No session or bad tcon")); | ||
2298 | if (pSesInfo->server) { | ||
2299 | spin_lock(&GlobalMid_Lock); | ||
2300 | srvTcp->tcpStatus = CifsExiting; | ||
2301 | spin_unlock(&GlobalMid_Lock); | ||
2302 | kill_cifsd(pSesInfo->server); | ||
2303 | } | ||
2304 | } | ||
2305 | sesInfoFree(pSesInfo); | ||
2306 | /* pSesInfo = NULL; */ | ||
2307 | } | ||
2308 | } | ||
2309 | } else { | ||
2310 | atomic_inc(&tcon->useCount); | ||
2311 | cifs_sb->tcon = tcon; | ||
2312 | tcon->ses = pSesInfo; | ||
2313 | |||
2314 | /* do not care if following two calls succeed - informational */ | ||
2315 | if (!tcon->ipc) { | ||
2316 | CIFSSMBQFSDeviceInfo(xid, tcon); | ||
2317 | CIFSSMBQFSAttributeInfo(xid, tcon); | ||
2318 | } | ||
2319 | |||
2320 | /* tell server which Unix caps we support */ | ||
2321 | if (tcon->ses->capabilities & CAP_UNIX) | ||
2322 | /* reset of caps checks mount to see if unix extensions | ||
2323 | disabled for just this mount */ | ||
2324 | reset_cifs_unix_caps(xid, tcon, sb, &volume_info); | ||
2325 | else | 2339 | else |
2326 | tcon->unix_ext = 0; /* server does not support them */ | 2340 | cifs_put_tcp_session(srvTcp); |
2341 | goto out; | ||
2342 | } | ||
2343 | cifs_sb->tcon = tcon; | ||
2327 | 2344 | ||
2328 | /* convert forward to back slashes in prepath here if needed */ | 2345 | /* do not care if following two calls succeed - informational */ |
2329 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) | 2346 | if (!tcon->ipc) { |
2330 | convert_delimiter(cifs_sb->prepath, | 2347 | CIFSSMBQFSDeviceInfo(xid, tcon); |
2331 | CIFS_DIR_SEP(cifs_sb)); | 2348 | CIFSSMBQFSAttributeInfo(xid, tcon); |
2349 | } | ||
2332 | 2350 | ||
2333 | if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { | 2351 | /* tell server which Unix caps we support */ |
2334 | cifs_sb->rsize = 1024 * 127; | 2352 | if (tcon->ses->capabilities & CAP_UNIX) |
2335 | cFYI(DBG2, | 2353 | /* reset of caps checks mount to see if unix extensions |
2336 | ("no very large read support, rsize now 127K")); | 2354 | disabled for just this mount */ |
2337 | } | 2355 | reset_cifs_unix_caps(xid, tcon, sb, &volume_info); |
2338 | if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) | 2356 | else |
2339 | cifs_sb->wsize = min(cifs_sb->wsize, | 2357 | tcon->unix_ext = 0; /* server does not support them */ |
2340 | (tcon->ses->server->maxBuf - | 2358 | |
2341 | MAX_CIFS_HDR_SIZE)); | 2359 | /* convert forward to back slashes in prepath here if needed */ |
2342 | if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) | 2360 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) |
2343 | cifs_sb->rsize = min(cifs_sb->rsize, | 2361 | convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); |
2344 | (tcon->ses->server->maxBuf - | 2362 | |
2345 | MAX_CIFS_HDR_SIZE)); | 2363 | if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { |
2364 | cifs_sb->rsize = 1024 * 127; | ||
2365 | cFYI(DBG2, ("no very large read support, rsize now 127K")); | ||
2346 | } | 2366 | } |
2367 | if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) | ||
2368 | cifs_sb->wsize = min(cifs_sb->wsize, | ||
2369 | (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); | ||
2370 | if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) | ||
2371 | cifs_sb->rsize = min(cifs_sb->rsize, | ||
2372 | (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); | ||
2347 | 2373 | ||
2348 | /* volume_info.password is freed above when existing session found | 2374 | /* volume_info.password is freed above when existing session found |
2349 | (in which case it is not needed anymore) but when new sesion is created | 2375 | (in which case it is not needed anymore) but when new sesion is created |
@@ -3513,6 +3539,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, | |||
3513 | /* above now done in SendReceive */ | 3539 | /* above now done in SendReceive */ |
3514 | if ((rc == 0) && (tcon != NULL)) { | 3540 | if ((rc == 0) && (tcon != NULL)) { |
3515 | tcon->tidStatus = CifsGood; | 3541 | tcon->tidStatus = CifsGood; |
3542 | tcon->need_reconnect = false; | ||
3516 | tcon->tid = smb_buffer_response->Tid; | 3543 | tcon->tid = smb_buffer_response->Tid; |
3517 | bcc_ptr = pByteArea(smb_buffer_response); | 3544 | bcc_ptr = pByteArea(smb_buffer_response); |
3518 | length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); | 3545 | length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); |
@@ -3584,48 +3611,17 @@ int | |||
3584 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | 3611 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) |
3585 | { | 3612 | { |
3586 | int rc = 0; | 3613 | int rc = 0; |
3587 | int xid; | ||
3588 | struct cifsSesInfo *ses = NULL; | ||
3589 | char *tmp; | 3614 | char *tmp; |
3590 | 3615 | ||
3591 | xid = GetXid(); | 3616 | if (cifs_sb->tcon) |
3592 | 3617 | cifs_put_tcon(cifs_sb->tcon); | |
3593 | if (cifs_sb->tcon) { | ||
3594 | ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/ | ||
3595 | rc = CIFSSMBTDis(xid, cifs_sb->tcon); | ||
3596 | if (rc == -EBUSY) { | ||
3597 | FreeXid(xid); | ||
3598 | return 0; | ||
3599 | } | ||
3600 | DeleteTconOplockQEntries(cifs_sb->tcon); | ||
3601 | tconInfoFree(cifs_sb->tcon); | ||
3602 | if ((ses) && (ses->server)) { | ||
3603 | /* save off task so we do not refer to ses later */ | ||
3604 | cFYI(1, ("About to do SMBLogoff ")); | ||
3605 | rc = CIFSSMBLogoff(xid, ses); | ||
3606 | if (rc == -EBUSY) { | ||
3607 | FreeXid(xid); | ||
3608 | return 0; | ||
3609 | } else if (rc == -ESHUTDOWN) { | ||
3610 | cFYI(1, ("Waking up socket by sending signal")); | ||
3611 | if (ses->server) | ||
3612 | kill_cifsd(ses->server); | ||
3613 | rc = 0; | ||
3614 | } /* else - we have an smb session | ||
3615 | left on this socket do not kill cifsd */ | ||
3616 | } else | ||
3617 | cFYI(1, ("No session or bad tcon")); | ||
3618 | } | ||
3619 | 3618 | ||
3620 | cifs_sb->tcon = NULL; | 3619 | cifs_sb->tcon = NULL; |
3621 | tmp = cifs_sb->prepath; | 3620 | tmp = cifs_sb->prepath; |
3622 | cifs_sb->prepathlen = 0; | 3621 | cifs_sb->prepathlen = 0; |
3623 | cifs_sb->prepath = NULL; | 3622 | cifs_sb->prepath = NULL; |
3624 | kfree(tmp); | 3623 | kfree(tmp); |
3625 | if (ses) | ||
3626 | sesInfoFree(ses); | ||
3627 | 3624 | ||
3628 | FreeXid(xid); | ||
3629 | return rc; | 3625 | return rc; |
3630 | } | 3626 | } |
3631 | 3627 | ||
@@ -3741,6 +3737,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, | |||
3741 | cFYI(1, ("CIFS Session Established successfully")); | 3737 | cFYI(1, ("CIFS Session Established successfully")); |
3742 | spin_lock(&GlobalMid_Lock); | 3738 | spin_lock(&GlobalMid_Lock); |
3743 | pSesInfo->status = CifsGood; | 3739 | pSesInfo->status = CifsGood; |
3740 | pSesInfo->need_reconnect = false; | ||
3744 | spin_unlock(&GlobalMid_Lock); | 3741 | spin_unlock(&GlobalMid_Lock); |
3745 | } | 3742 | } |
3746 | 3743 | ||
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ead1a3bb0256..6449e1aae621 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -493,7 +493,7 @@ int cifs_close(struct inode *inode, struct file *file) | |||
493 | if (pTcon) { | 493 | if (pTcon) { |
494 | /* no sense reconnecting to close a file that is | 494 | /* no sense reconnecting to close a file that is |
495 | already closed */ | 495 | already closed */ |
496 | if (pTcon->tidStatus != CifsNeedReconnect) { | 496 | if (!pTcon->need_reconnect) { |
497 | timeout = 2; | 497 | timeout = 2; |
498 | while ((atomic_read(&pSMBFile->wrtPending) != 0) | 498 | while ((atomic_read(&pSMBFile->wrtPending) != 0) |
499 | && (timeout <= 2048)) { | 499 | && (timeout <= 2048)) { |
@@ -1404,7 +1404,10 @@ retry: | |||
1404 | if ((wbc->nr_to_write -= n_iov) <= 0) | 1404 | if ((wbc->nr_to_write -= n_iov) <= 0) |
1405 | done = 1; | 1405 | done = 1; |
1406 | index = next; | 1406 | index = next; |
1407 | } | 1407 | } else |
1408 | /* Need to re-find the pages we skipped */ | ||
1409 | index = pvec.pages[0]->index + 1; | ||
1410 | |||
1408 | pagevec_release(&pvec); | 1411 | pagevec_release(&pvec); |
1409 | } | 1412 | } |
1410 | if (!scanned && !done) { | 1413 | if (!scanned && !done) { |
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index ec36410a9125..f108040ca1bc 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -75,12 +75,12 @@ sesInfoAlloc(void) | |||
75 | 75 | ||
76 | ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); | 76 | ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); |
77 | if (ret_buf) { | 77 | if (ret_buf) { |
78 | write_lock(&GlobalSMBSeslock); | ||
79 | atomic_inc(&sesInfoAllocCount); | 78 | atomic_inc(&sesInfoAllocCount); |
80 | ret_buf->status = CifsNew; | 79 | ret_buf->status = CifsNew; |
81 | list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList); | 80 | ++ret_buf->ses_count; |
81 | INIT_LIST_HEAD(&ret_buf->smb_ses_list); | ||
82 | INIT_LIST_HEAD(&ret_buf->tcon_list); | ||
82 | init_MUTEX(&ret_buf->sesSem); | 83 | init_MUTEX(&ret_buf->sesSem); |
83 | write_unlock(&GlobalSMBSeslock); | ||
84 | } | 84 | } |
85 | return ret_buf; | 85 | return ret_buf; |
86 | } | 86 | } |
@@ -93,10 +93,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free) | |||
93 | return; | 93 | return; |
94 | } | 94 | } |
95 | 95 | ||
96 | write_lock(&GlobalSMBSeslock); | ||
97 | atomic_dec(&sesInfoAllocCount); | 96 | atomic_dec(&sesInfoAllocCount); |
98 | list_del(&buf_to_free->cifsSessionList); | ||
99 | write_unlock(&GlobalSMBSeslock); | ||
100 | kfree(buf_to_free->serverOS); | 97 | kfree(buf_to_free->serverOS); |
101 | kfree(buf_to_free->serverDomain); | 98 | kfree(buf_to_free->serverDomain); |
102 | kfree(buf_to_free->serverNOS); | 99 | kfree(buf_to_free->serverNOS); |
@@ -111,17 +108,14 @@ tconInfoAlloc(void) | |||
111 | struct cifsTconInfo *ret_buf; | 108 | struct cifsTconInfo *ret_buf; |
112 | ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); | 109 | ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); |
113 | if (ret_buf) { | 110 | if (ret_buf) { |
114 | write_lock(&GlobalSMBSeslock); | ||
115 | atomic_inc(&tconInfoAllocCount); | 111 | atomic_inc(&tconInfoAllocCount); |
116 | list_add(&ret_buf->cifsConnectionList, | ||
117 | &GlobalTreeConnectionList); | ||
118 | ret_buf->tidStatus = CifsNew; | 112 | ret_buf->tidStatus = CifsNew; |
113 | ++ret_buf->tc_count; | ||
119 | INIT_LIST_HEAD(&ret_buf->openFileList); | 114 | INIT_LIST_HEAD(&ret_buf->openFileList); |
120 | init_MUTEX(&ret_buf->tconSem); | 115 | INIT_LIST_HEAD(&ret_buf->tcon_list); |
121 | #ifdef CONFIG_CIFS_STATS | 116 | #ifdef CONFIG_CIFS_STATS |
122 | spin_lock_init(&ret_buf->stat_lock); | 117 | spin_lock_init(&ret_buf->stat_lock); |
123 | #endif | 118 | #endif |
124 | write_unlock(&GlobalSMBSeslock); | ||
125 | } | 119 | } |
126 | return ret_buf; | 120 | return ret_buf; |
127 | } | 121 | } |
@@ -133,10 +127,7 @@ tconInfoFree(struct cifsTconInfo *buf_to_free) | |||
133 | cFYI(1, ("Null buffer passed to tconInfoFree")); | 127 | cFYI(1, ("Null buffer passed to tconInfoFree")); |
134 | return; | 128 | return; |
135 | } | 129 | } |
136 | write_lock(&GlobalSMBSeslock); | ||
137 | atomic_dec(&tconInfoAllocCount); | 130 | atomic_dec(&tconInfoAllocCount); |
138 | list_del(&buf_to_free->cifsConnectionList); | ||
139 | write_unlock(&GlobalSMBSeslock); | ||
140 | kfree(buf_to_free->nativeFileSystem); | 131 | kfree(buf_to_free->nativeFileSystem); |
141 | kfree(buf_to_free); | 132 | kfree(buf_to_free); |
142 | } | 133 | } |
@@ -350,9 +341,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , | |||
350 | if (current_fsuid() != treeCon->ses->linux_uid) { | 341 | if (current_fsuid() != treeCon->ses->linux_uid) { |
351 | cFYI(1, ("Multiuser mode and UID " | 342 | cFYI(1, ("Multiuser mode and UID " |
352 | "did not match tcon uid")); | 343 | "did not match tcon uid")); |
353 | read_lock(&GlobalSMBSeslock); | 344 | read_lock(&cifs_tcp_ses_lock); |
354 | list_for_each(temp_item, &GlobalSMBSessionList) { | 345 | list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) { |
355 | ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList); | 346 | ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list); |
356 | if (ses->linux_uid == current_fsuid()) { | 347 | if (ses->linux_uid == current_fsuid()) { |
357 | if (ses->server == treeCon->ses->server) { | 348 | if (ses->server == treeCon->ses->server) { |
358 | cFYI(1, ("found matching uid substitute right smb_uid")); | 349 | cFYI(1, ("found matching uid substitute right smb_uid")); |
@@ -364,7 +355,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , | |||
364 | } | 355 | } |
365 | } | 356 | } |
366 | } | 357 | } |
367 | read_unlock(&GlobalSMBSeslock); | 358 | read_unlock(&cifs_tcp_ses_lock); |
368 | } | 359 | } |
369 | } | 360 | } |
370 | } | 361 | } |
@@ -497,9 +488,10 @@ bool | |||
497 | is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) | 488 | is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) |
498 | { | 489 | { |
499 | struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; | 490 | struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; |
500 | struct list_head *tmp; | 491 | struct list_head *tmp, *tmp1, *tmp2; |
501 | struct list_head *tmp1; | 492 | struct cifsSesInfo *ses; |
502 | struct cifsTconInfo *tcon; | 493 | struct cifsTconInfo *tcon; |
494 | struct cifsInodeInfo *pCifsInode; | ||
503 | struct cifsFileInfo *netfile; | 495 | struct cifsFileInfo *netfile; |
504 | 496 | ||
505 | cFYI(1, ("Checking for oplock break or dnotify response")); | 497 | cFYI(1, ("Checking for oplock break or dnotify response")); |
@@ -554,42 +546,42 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) | |||
554 | return false; | 546 | return false; |
555 | 547 | ||
556 | /* look up tcon based on tid & uid */ | 548 | /* look up tcon based on tid & uid */ |
557 | read_lock(&GlobalSMBSeslock); | 549 | read_lock(&cifs_tcp_ses_lock); |
558 | list_for_each(tmp, &GlobalTreeConnectionList) { | 550 | list_for_each(tmp, &srv->smb_ses_list) { |
559 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | 551 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
560 | if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) { | 552 | list_for_each(tmp1, &ses->tcon_list) { |
553 | tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list); | ||
554 | if (tcon->tid != buf->Tid) | ||
555 | continue; | ||
556 | |||
561 | cifs_stats_inc(&tcon->num_oplock_brks); | 557 | cifs_stats_inc(&tcon->num_oplock_brks); |
562 | list_for_each(tmp1, &tcon->openFileList) { | 558 | list_for_each(tmp2, &tcon->openFileList) { |
563 | netfile = list_entry(tmp1, struct cifsFileInfo, | 559 | netfile = list_entry(tmp2, struct cifsFileInfo, |
564 | tlist); | 560 | tlist); |
565 | if (pSMB->Fid == netfile->netfid) { | 561 | if (pSMB->Fid != netfile->netfid) |
566 | struct cifsInodeInfo *pCifsInode; | 562 | continue; |
567 | read_unlock(&GlobalSMBSeslock); | 563 | |
568 | cFYI(1, | 564 | read_unlock(&cifs_tcp_ses_lock); |
569 | ("file id match, oplock break")); | 565 | cFYI(1, ("file id match, oplock break")); |
570 | pCifsInode = | 566 | pCifsInode = CIFS_I(netfile->pInode); |
571 | CIFS_I(netfile->pInode); | 567 | pCifsInode->clientCanCacheAll = false; |
572 | pCifsInode->clientCanCacheAll = false; | 568 | if (pSMB->OplockLevel == 0) |
573 | if (pSMB->OplockLevel == 0) | 569 | pCifsInode->clientCanCacheRead = false; |
574 | pCifsInode->clientCanCacheRead | 570 | pCifsInode->oplockPending = true; |
575 | = false; | 571 | AllocOplockQEntry(netfile->pInode, |
576 | pCifsInode->oplockPending = true; | 572 | netfile->netfid, tcon); |
577 | AllocOplockQEntry(netfile->pInode, | 573 | cFYI(1, ("about to wake up oplock thread")); |
578 | netfile->netfid, | 574 | if (oplockThread) |
579 | tcon); | 575 | wake_up_process(oplockThread); |
580 | cFYI(1, | 576 | |
581 | ("about to wake up oplock thread")); | 577 | return true; |
582 | if (oplockThread) | ||
583 | wake_up_process(oplockThread); | ||
584 | return true; | ||
585 | } | ||
586 | } | 578 | } |
587 | read_unlock(&GlobalSMBSeslock); | 579 | read_unlock(&cifs_tcp_ses_lock); |
588 | cFYI(1, ("No matching file for oplock break")); | 580 | cFYI(1, ("No matching file for oplock break")); |
589 | return true; | 581 | return true; |
590 | } | 582 | } |
591 | } | 583 | } |
592 | read_unlock(&GlobalSMBSeslock); | 584 | read_unlock(&cifs_tcp_ses_lock); |
593 | cFYI(1, ("Can not process oplock break for non-existent connection")); | 585 | cFYI(1, ("Can not process oplock break for non-existent connection")); |
594 | return true; | 586 | return true; |
595 | } | 587 | } |
diff --git a/fs/inotify.c b/fs/inotify.c index 690e72595e6e..7bbed1b89825 100644 --- a/fs/inotify.c +++ b/fs/inotify.c | |||
@@ -106,6 +106,20 @@ void get_inotify_watch(struct inotify_watch *watch) | |||
106 | } | 106 | } |
107 | EXPORT_SYMBOL_GPL(get_inotify_watch); | 107 | EXPORT_SYMBOL_GPL(get_inotify_watch); |
108 | 108 | ||
109 | int pin_inotify_watch(struct inotify_watch *watch) | ||
110 | { | ||
111 | struct super_block *sb = watch->inode->i_sb; | ||
112 | spin_lock(&sb_lock); | ||
113 | if (sb->s_count >= S_BIAS) { | ||
114 | atomic_inc(&sb->s_active); | ||
115 | spin_unlock(&sb_lock); | ||
116 | atomic_inc(&watch->count); | ||
117 | return 1; | ||
118 | } | ||
119 | spin_unlock(&sb_lock); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
109 | /** | 123 | /** |
110 | * put_inotify_watch - decrements the ref count on a given watch. cleans up | 124 | * put_inotify_watch - decrements the ref count on a given watch. cleans up |
111 | * watch references if the count reaches zero. inotify_watch is freed by | 125 | * watch references if the count reaches zero. inotify_watch is freed by |
@@ -124,6 +138,13 @@ void put_inotify_watch(struct inotify_watch *watch) | |||
124 | } | 138 | } |
125 | EXPORT_SYMBOL_GPL(put_inotify_watch); | 139 | EXPORT_SYMBOL_GPL(put_inotify_watch); |
126 | 140 | ||
141 | void unpin_inotify_watch(struct inotify_watch *watch) | ||
142 | { | ||
143 | struct super_block *sb = watch->inode->i_sb; | ||
144 | put_inotify_watch(watch); | ||
145 | deactivate_super(sb); | ||
146 | } | ||
147 | |||
127 | /* | 148 | /* |
128 | * inotify_handle_get_wd - returns the next WD for use by the given handle | 149 | * inotify_handle_get_wd - returns the next WD for use by the given handle |
129 | * | 150 | * |
@@ -479,6 +500,112 @@ void inotify_init_watch(struct inotify_watch *watch) | |||
479 | } | 500 | } |
480 | EXPORT_SYMBOL_GPL(inotify_init_watch); | 501 | EXPORT_SYMBOL_GPL(inotify_init_watch); |
481 | 502 | ||
503 | /* | ||
504 | * Watch removals suck violently. To kick the watch out we need (in this | ||
505 | * order) inode->inotify_mutex and ih->mutex. That's fine if we have | ||
506 | * a hold on inode; however, for all other cases we need to make damn sure | ||
507 | * we don't race with umount. We can *NOT* just grab a reference to a | ||
508 | * watch - inotify_unmount_inodes() will happily sail past it and we'll end | ||
509 | * with reference to inode potentially outliving its superblock. Ideally | ||
510 | * we just want to grab an active reference to superblock if we can; that | ||
511 | * will make sure we won't go into inotify_umount_inodes() until we are | ||
512 | * done. Cleanup is just deactivate_super(). However, that leaves a messy | ||
513 | * case - what if we *are* racing with umount() and active references to | ||
514 | * superblock can't be acquired anymore? We can bump ->s_count, grab | ||
515 | * ->s_umount, which will almost certainly wait until the superblock is shut | ||
516 | * down and the watch in question is pining for fjords. That's fine, but | ||
517 | * there is a problem - we might have hit the window between ->s_active | ||
518 | * getting to 0 / ->s_count - below S_BIAS (i.e. the moment when superblock | ||
519 | * is past the point of no return and is heading for shutdown) and the | ||
520 | * moment when deactivate_super() acquires ->s_umount. We could just do | ||
521 | * drop_super() yield() and retry, but that's rather antisocial and this | ||
522 | * stuff is luser-triggerable. OTOH, having grabbed ->s_umount and having | ||
523 | * found that we'd got there first (i.e. that ->s_root is non-NULL) we know | ||
524 | * that we won't race with inotify_umount_inodes(). So we could grab a | ||
525 | * reference to watch and do the rest as above, just with drop_super() instead | ||
526 | * of deactivate_super(), right? Wrong. We had to drop ih->mutex before we | ||
527 | * could grab ->s_umount. So the watch could've been gone already. | ||
528 | * | ||
529 | * That still can be dealt with - we need to save watch->wd, do idr_find() | ||
530 | * and compare its result with our pointer. If they match, we either have | ||
531 | * the damn thing still alive or we'd lost not one but two races at once, | ||
532 | * the watch had been killed and a new one got created with the same ->wd | ||
533 | * at the same address. That couldn't have happened in inotify_destroy(), | ||
534 | * but inotify_rm_wd() could run into that. Still, "new one got created" | ||
535 | * is not a problem - we have every right to kill it or leave it alone, | ||
536 | * whatever's more convenient. | ||
537 | * | ||
538 | * So we can use idr_find(...) == watch && watch->inode->i_sb == sb as | ||
539 | * "grab it and kill it" check. If it's been our original watch, we are | ||
540 | * fine, if it's a newcomer - nevermind, just pretend that we'd won the | ||
541 | * race and kill the fscker anyway; we are safe since we know that its | ||
542 | * superblock won't be going away. | ||
543 | * | ||
544 | * And yes, this is far beyond mere "not very pretty"; so's the entire | ||
545 | * concept of inotify to start with. | ||
546 | */ | ||
547 | |||
548 | /** | ||
549 | * pin_to_kill - pin the watch down for removal | ||
550 | * @ih: inotify handle | ||
551 | * @watch: watch to kill | ||
552 | * | ||
553 | * Called with ih->mutex held, drops it. Possible return values: | ||
554 | * 0 - nothing to do, it has died | ||
555 | * 1 - remove it, drop the reference and deactivate_super() | ||
556 | * 2 - remove it, drop the reference and drop_super(); we tried hard to avoid | ||
557 | * that variant, since it involved a lot of PITA, but that's the best that | ||
558 | * could've been done. | ||
559 | */ | ||
560 | static int pin_to_kill(struct inotify_handle *ih, struct inotify_watch *watch) | ||
561 | { | ||
562 | struct super_block *sb = watch->inode->i_sb; | ||
563 | s32 wd = watch->wd; | ||
564 | |||
565 | spin_lock(&sb_lock); | ||
566 | if (sb->s_count >= S_BIAS) { | ||
567 | atomic_inc(&sb->s_active); | ||
568 | spin_unlock(&sb_lock); | ||
569 | get_inotify_watch(watch); | ||
570 | mutex_unlock(&ih->mutex); | ||
571 | return 1; /* the best outcome */ | ||
572 | } | ||
573 | sb->s_count++; | ||
574 | spin_unlock(&sb_lock); | ||
575 | mutex_unlock(&ih->mutex); /* can't grab ->s_umount under it */ | ||
576 | down_read(&sb->s_umount); | ||
577 | if (likely(!sb->s_root)) { | ||
578 | /* fs is already shut down; the watch is dead */ | ||
579 | drop_super(sb); | ||
580 | return 0; | ||
581 | } | ||
582 | /* raced with the final deactivate_super() */ | ||
583 | mutex_lock(&ih->mutex); | ||
584 | if (idr_find(&ih->idr, wd) != watch || watch->inode->i_sb != sb) { | ||
585 | /* the watch is dead */ | ||
586 | mutex_unlock(&ih->mutex); | ||
587 | drop_super(sb); | ||
588 | return 0; | ||
589 | } | ||
590 | /* still alive or freed and reused with the same sb and wd; kill */ | ||
591 | get_inotify_watch(watch); | ||
592 | mutex_unlock(&ih->mutex); | ||
593 | return 2; | ||
594 | } | ||
595 | |||
596 | static void unpin_and_kill(struct inotify_watch *watch, int how) | ||
597 | { | ||
598 | struct super_block *sb = watch->inode->i_sb; | ||
599 | put_inotify_watch(watch); | ||
600 | switch (how) { | ||
601 | case 1: | ||
602 | deactivate_super(sb); | ||
603 | break; | ||
604 | case 2: | ||
605 | drop_super(sb); | ||
606 | } | ||
607 | } | ||
608 | |||
482 | /** | 609 | /** |
483 | * inotify_destroy - clean up and destroy an inotify instance | 610 | * inotify_destroy - clean up and destroy an inotify instance |
484 | * @ih: inotify handle | 611 | * @ih: inotify handle |
@@ -490,11 +617,15 @@ void inotify_destroy(struct inotify_handle *ih) | |||
490 | * pretty. We cannot do a simple iteration over the list, because we | 617 | * pretty. We cannot do a simple iteration over the list, because we |
491 | * do not know the inode until we iterate to the watch. But we need to | 618 | * do not know the inode until we iterate to the watch. But we need to |
492 | * hold inode->inotify_mutex before ih->mutex. The following works. | 619 | * hold inode->inotify_mutex before ih->mutex. The following works. |
620 | * | ||
621 | * AV: it had to become even uglier to start working ;-/ | ||
493 | */ | 622 | */ |
494 | while (1) { | 623 | while (1) { |
495 | struct inotify_watch *watch; | 624 | struct inotify_watch *watch; |
496 | struct list_head *watches; | 625 | struct list_head *watches; |
626 | struct super_block *sb; | ||
497 | struct inode *inode; | 627 | struct inode *inode; |
628 | int how; | ||
498 | 629 | ||
499 | mutex_lock(&ih->mutex); | 630 | mutex_lock(&ih->mutex); |
500 | watches = &ih->watches; | 631 | watches = &ih->watches; |
@@ -503,8 +634,10 @@ void inotify_destroy(struct inotify_handle *ih) | |||
503 | break; | 634 | break; |
504 | } | 635 | } |
505 | watch = list_first_entry(watches, struct inotify_watch, h_list); | 636 | watch = list_first_entry(watches, struct inotify_watch, h_list); |
506 | get_inotify_watch(watch); | 637 | sb = watch->inode->i_sb; |
507 | mutex_unlock(&ih->mutex); | 638 | how = pin_to_kill(ih, watch); |
639 | if (!how) | ||
640 | continue; | ||
508 | 641 | ||
509 | inode = watch->inode; | 642 | inode = watch->inode; |
510 | mutex_lock(&inode->inotify_mutex); | 643 | mutex_lock(&inode->inotify_mutex); |
@@ -518,7 +651,7 @@ void inotify_destroy(struct inotify_handle *ih) | |||
518 | 651 | ||
519 | mutex_unlock(&ih->mutex); | 652 | mutex_unlock(&ih->mutex); |
520 | mutex_unlock(&inode->inotify_mutex); | 653 | mutex_unlock(&inode->inotify_mutex); |
521 | put_inotify_watch(watch); | 654 | unpin_and_kill(watch, how); |
522 | } | 655 | } |
523 | 656 | ||
524 | /* free this handle: the put matching the get in inotify_init() */ | 657 | /* free this handle: the put matching the get in inotify_init() */ |
@@ -719,7 +852,9 @@ void inotify_evict_watch(struct inotify_watch *watch) | |||
719 | int inotify_rm_wd(struct inotify_handle *ih, u32 wd) | 852 | int inotify_rm_wd(struct inotify_handle *ih, u32 wd) |
720 | { | 853 | { |
721 | struct inotify_watch *watch; | 854 | struct inotify_watch *watch; |
855 | struct super_block *sb; | ||
722 | struct inode *inode; | 856 | struct inode *inode; |
857 | int how; | ||
723 | 858 | ||
724 | mutex_lock(&ih->mutex); | 859 | mutex_lock(&ih->mutex); |
725 | watch = idr_find(&ih->idr, wd); | 860 | watch = idr_find(&ih->idr, wd); |
@@ -727,9 +862,12 @@ int inotify_rm_wd(struct inotify_handle *ih, u32 wd) | |||
727 | mutex_unlock(&ih->mutex); | 862 | mutex_unlock(&ih->mutex); |
728 | return -EINVAL; | 863 | return -EINVAL; |
729 | } | 864 | } |
730 | get_inotify_watch(watch); | 865 | sb = watch->inode->i_sb; |
866 | how = pin_to_kill(ih, watch); | ||
867 | if (!how) | ||
868 | return 0; | ||
869 | |||
731 | inode = watch->inode; | 870 | inode = watch->inode; |
732 | mutex_unlock(&ih->mutex); | ||
733 | 871 | ||
734 | mutex_lock(&inode->inotify_mutex); | 872 | mutex_lock(&inode->inotify_mutex); |
735 | mutex_lock(&ih->mutex); | 873 | mutex_lock(&ih->mutex); |
@@ -740,7 +878,7 @@ int inotify_rm_wd(struct inotify_handle *ih, u32 wd) | |||
740 | 878 | ||
741 | mutex_unlock(&ih->mutex); | 879 | mutex_unlock(&ih->mutex); |
742 | mutex_unlock(&inode->inotify_mutex); | 880 | mutex_unlock(&inode->inotify_mutex); |
743 | put_inotify_watch(watch); | 881 | unpin_and_kill(watch, how); |
744 | 882 | ||
745 | return 0; | 883 | return 0; |
746 | } | 884 | } |
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 94fcfff6863a..06ed10b7da9e 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
@@ -31,6 +31,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb, | |||
31 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | 31 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; |
32 | inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */ | 32 | inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */ |
33 | inode->i_mode = table->mode; | 33 | inode->i_mode = table->mode; |
34 | inode->i_uid = inode->i_gid = 0; | ||
34 | if (!table->child) { | 35 | if (!table->child) { |
35 | inode->i_mode |= S_IFREG; | 36 | inode->i_mode |= S_IFREG; |
36 | inode->i_op = &proc_sys_inode_operations; | 37 | inode->i_op = &proc_sys_inode_operations; |