diff options
Diffstat (limited to 'fs/cifs/dir.c')
| -rw-r--r-- | fs/cifs/dir.c | 307 |
1 files changed, 205 insertions, 102 deletions
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 964aad03c5ad..89fb72832652 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * | 3 | * |
| 4 | * vfs operations that deal with dentries | 4 | * vfs operations that deal with dentries |
| 5 | * | 5 | * |
| 6 | * Copyright (C) International Business Machines Corp., 2002,2008 | 6 | * Copyright (C) International Business Machines Corp., 2002,2009 |
| 7 | * Author(s): Steve French (sfrench@us.ibm.com) | 7 | * Author(s): Steve French (sfrench@us.ibm.com) |
| 8 | * | 8 | * |
| 9 | * This library is free software; you can redistribute it and/or modify | 9 | * This library is free software; you can redistribute it and/or modify |
| @@ -129,6 +129,78 @@ cifs_bp_rename_retry: | |||
| 129 | return full_path; | 129 | return full_path; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | static int cifs_posix_open(char *full_path, struct inode **pinode, | ||
| 133 | struct super_block *sb, int mode, int oflags, | ||
| 134 | int *poplock, __u16 *pnetfid, int xid) | ||
| 135 | { | ||
| 136 | int rc; | ||
| 137 | __u32 oplock; | ||
| 138 | FILE_UNIX_BASIC_INFO *presp_data; | ||
| 139 | __u32 posix_flags = 0; | ||
| 140 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | ||
| 141 | |||
| 142 | cFYI(1, ("posix open %s", full_path)); | ||
| 143 | |||
| 144 | presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); | ||
| 145 | if (presp_data == NULL) | ||
| 146 | return -ENOMEM; | ||
| 147 | |||
| 148 | /* So far cifs posix extensions can only map the following flags. | ||
| 149 | There are other valid fmode oflags such as FMODE_LSEEK, FMODE_PREAD, but | ||
| 150 | so far we do not seem to need them, and we can treat them as local only */ | ||
| 151 | if ((oflags & (FMODE_READ | FMODE_WRITE)) == | ||
| 152 | (FMODE_READ | FMODE_WRITE)) | ||
| 153 | posix_flags = SMB_O_RDWR; | ||
| 154 | else if (oflags & FMODE_READ) | ||
| 155 | posix_flags = SMB_O_RDONLY; | ||
| 156 | else if (oflags & FMODE_WRITE) | ||
| 157 | posix_flags = SMB_O_WRONLY; | ||
| 158 | if (oflags & O_CREAT) | ||
| 159 | posix_flags |= SMB_O_CREAT; | ||
| 160 | if (oflags & O_EXCL) | ||
| 161 | posix_flags |= SMB_O_EXCL; | ||
| 162 | if (oflags & O_TRUNC) | ||
| 163 | posix_flags |= SMB_O_TRUNC; | ||
| 164 | if (oflags & O_APPEND) | ||
| 165 | posix_flags |= SMB_O_APPEND; | ||
| 166 | if (oflags & O_SYNC) | ||
| 167 | posix_flags |= SMB_O_SYNC; | ||
| 168 | if (oflags & O_DIRECTORY) | ||
| 169 | posix_flags |= SMB_O_DIRECTORY; | ||
| 170 | if (oflags & O_NOFOLLOW) | ||
| 171 | posix_flags |= SMB_O_NOFOLLOW; | ||
| 172 | if (oflags & O_DIRECT) | ||
| 173 | posix_flags |= SMB_O_DIRECT; | ||
| 174 | |||
| 175 | |||
| 176 | rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode, | ||
| 177 | pnetfid, presp_data, &oplock, full_path, | ||
| 178 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | ||
| 179 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 180 | if (rc) | ||
| 181 | goto posix_open_ret; | ||
| 182 | |||
| 183 | if (presp_data->Type == cpu_to_le32(-1)) | ||
| 184 | goto posix_open_ret; /* open ok, caller does qpathinfo */ | ||
| 185 | |||
| 186 | /* get new inode and set it up */ | ||
| 187 | if (!pinode) | ||
| 188 | goto posix_open_ret; /* caller does not need info */ | ||
| 189 | |||
| 190 | *pinode = cifs_new_inode(sb, &presp_data->UniqueId); | ||
| 191 | |||
| 192 | /* We do not need to close the file if new_inode fails since | ||
| 193 | the caller will retry qpathinfo as long as inode is null */ | ||
| 194 | if (*pinode == NULL) | ||
| 195 | goto posix_open_ret; | ||
| 196 | |||
| 197 | posix_fill_in_inode(*pinode, presp_data, 1); | ||
| 198 | |||
| 199 | posix_open_ret: | ||
| 200 | kfree(presp_data); | ||
| 201 | return rc; | ||
| 202 | } | ||
| 203 | |||
| 132 | static void setup_cifs_dentry(struct cifsTconInfo *tcon, | 204 | static void setup_cifs_dentry(struct cifsTconInfo *tcon, |
| 133 | struct dentry *direntry, | 205 | struct dentry *direntry, |
| 134 | struct inode *newinode) | 206 | struct inode *newinode) |
| @@ -150,7 +222,14 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, | |||
| 150 | int xid; | 222 | int xid; |
| 151 | int create_options = CREATE_NOT_DIR; | 223 | int create_options = CREATE_NOT_DIR; |
| 152 | int oplock = 0; | 224 | int oplock = 0; |
| 153 | /* BB below access is too much for the mknod to request */ | 225 | int oflags; |
| 226 | /* | ||
| 227 | * BB below access is probably too much for mknod to request | ||
| 228 | * but we have to do query and setpathinfo so requesting | ||
| 229 | * less could fail (unless we want to request getatr and setatr | ||
| 230 | * permissions (only). At least for POSIX we do not have to | ||
| 231 | * request so much. | ||
| 232 | */ | ||
| 154 | int desiredAccess = GENERIC_READ | GENERIC_WRITE; | 233 | int desiredAccess = GENERIC_READ | GENERIC_WRITE; |
| 155 | __u16 fileHandle; | 234 | __u16 fileHandle; |
| 156 | struct cifs_sb_info *cifs_sb; | 235 | struct cifs_sb_info *cifs_sb; |
| @@ -174,13 +253,43 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, | |||
| 174 | } | 253 | } |
| 175 | 254 | ||
| 176 | mode &= ~current->fs->umask; | 255 | mode &= ~current->fs->umask; |
| 256 | if (oplockEnabled) | ||
| 257 | oplock = REQ_OPLOCK; | ||
| 177 | 258 | ||
| 178 | if (nd && (nd->flags & LOOKUP_OPEN)) { | 259 | if (nd && (nd->flags & LOOKUP_OPEN)) |
| 179 | int oflags = nd->intent.open.flags; | 260 | oflags = nd->intent.open.flags; |
| 261 | else | ||
| 262 | oflags = FMODE_READ; | ||
| 263 | |||
| 264 | if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && | ||
| 265 | (CIFS_UNIX_POSIX_PATH_OPS_CAP & | ||
| 266 | le64_to_cpu(tcon->fsUnixInfo.Capability))) { | ||
| 267 | rc = cifs_posix_open(full_path, &newinode, inode->i_sb, | ||
| 268 | mode, oflags, &oplock, &fileHandle, xid); | ||
| 269 | /* EIO could indicate that (posix open) operation is not | ||
| 270 | supported, despite what server claimed in capability | ||
| 271 | negotation. EREMOTE indicates DFS junction, which is not | ||
| 272 | handled in posix open */ | ||
| 273 | |||
| 274 | if ((rc == 0) && (newinode == NULL)) | ||
| 275 | goto cifs_create_get_file_info; /* query inode info */ | ||
| 276 | else if (rc == 0) /* success, no need to query */ | ||
| 277 | goto cifs_create_set_dentry; | ||
| 278 | else if ((rc != -EIO) && (rc != -EREMOTE) && | ||
| 279 | (rc != -EOPNOTSUPP)) /* path not found or net err */ | ||
| 280 | goto cifs_create_out; | ||
| 281 | /* else fallthrough to retry, using older open call, this is | ||
| 282 | case where server does not support this SMB level, and | ||
| 283 | falsely claims capability (also get here for DFS case | ||
| 284 | which should be rare for path not covered on files) */ | ||
| 285 | } | ||
| 180 | 286 | ||
| 287 | if (nd && (nd->flags & LOOKUP_OPEN)) { | ||
| 288 | /* if the file is going to stay open, then we | ||
| 289 | need to set the desired access properly */ | ||
| 181 | desiredAccess = 0; | 290 | desiredAccess = 0; |
| 182 | if (oflags & FMODE_READ) | 291 | if (oflags & FMODE_READ) |
| 183 | desiredAccess |= GENERIC_READ; | 292 | desiredAccess |= GENERIC_READ; /* is this too little? */ |
| 184 | if (oflags & FMODE_WRITE) { | 293 | if (oflags & FMODE_WRITE) { |
| 185 | desiredAccess |= GENERIC_WRITE; | 294 | desiredAccess |= GENERIC_WRITE; |
| 186 | if (!(oflags & FMODE_READ)) | 295 | if (!(oflags & FMODE_READ)) |
| @@ -199,8 +308,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, | |||
| 199 | 308 | ||
| 200 | /* BB add processing to set equivalent of mode - e.g. via CreateX with | 309 | /* BB add processing to set equivalent of mode - e.g. via CreateX with |
| 201 | ACLs */ | 310 | ACLs */ |
| 202 | if (oplockEnabled) | ||
| 203 | oplock = REQ_OPLOCK; | ||
| 204 | 311 | ||
| 205 | buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); | 312 | buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); |
| 206 | if (buf == NULL) { | 313 | if (buf == NULL) { |
| @@ -233,116 +340,112 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, | |||
| 233 | } | 340 | } |
| 234 | if (rc) { | 341 | if (rc) { |
| 235 | cFYI(1, ("cifs_create returned 0x%x", rc)); | 342 | cFYI(1, ("cifs_create returned 0x%x", rc)); |
| 236 | } else { | 343 | goto cifs_create_out; |
| 237 | /* If Open reported that we actually created a file | 344 | } |
| 238 | then we now have to set the mode if possible */ | 345 | |
| 239 | if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) { | 346 | /* If Open reported that we actually created a file |
| 240 | struct cifs_unix_set_info_args args = { | 347 | then we now have to set the mode if possible */ |
| 348 | if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) { | ||
| 349 | struct cifs_unix_set_info_args args = { | ||
| 241 | .mode = mode, | 350 | .mode = mode, |
| 242 | .ctime = NO_CHANGE_64, | 351 | .ctime = NO_CHANGE_64, |
| 243 | .atime = NO_CHANGE_64, | 352 | .atime = NO_CHANGE_64, |
| 244 | .mtime = NO_CHANGE_64, | 353 | .mtime = NO_CHANGE_64, |
| 245 | .device = 0, | 354 | .device = 0, |
| 246 | }; | 355 | }; |
| 247 | 356 | ||
| 248 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { | 357 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { |
| 249 | args.uid = (__u64) current_fsuid(); | 358 | args.uid = (__u64) current_fsuid(); |
| 250 | if (inode->i_mode & S_ISGID) | 359 | if (inode->i_mode & S_ISGID) |
| 251 | args.gid = (__u64) inode->i_gid; | 360 | args.gid = (__u64) inode->i_gid; |
| 252 | else | 361 | else |
| 253 | args.gid = (__u64) current_fsgid(); | 362 | args.gid = (__u64) current_fsgid(); |
| 254 | } else { | ||
| 255 | args.uid = NO_CHANGE_64; | ||
| 256 | args.gid = NO_CHANGE_64; | ||
| 257 | } | ||
| 258 | CIFSSMBUnixSetInfo(xid, tcon, full_path, &args, | ||
| 259 | cifs_sb->local_nls, | ||
| 260 | cifs_sb->mnt_cifs_flags & | ||
| 261 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 262 | } else { | 363 | } else { |
| 263 | /* BB implement mode setting via Windows security | 364 | args.uid = NO_CHANGE_64; |
| 264 | descriptors e.g. */ | 365 | args.gid = NO_CHANGE_64; |
| 265 | /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ | ||
| 266 | |||
| 267 | /* Could set r/o dos attribute if mode & 0222 == 0 */ | ||
| 268 | } | 366 | } |
| 367 | CIFSSMBUnixSetInfo(xid, tcon, full_path, &args, | ||
| 368 | cifs_sb->local_nls, | ||
| 369 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 370 | } else { | ||
| 371 | /* BB implement mode setting via Windows security | ||
| 372 | descriptors e.g. */ | ||
| 373 | /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ | ||
| 269 | 374 | ||
| 270 | /* server might mask mode so we have to query for it */ | 375 | /* Could set r/o dos attribute if mode & 0222 == 0 */ |
| 271 | if (tcon->unix_ext) | 376 | } |
| 272 | rc = cifs_get_inode_info_unix(&newinode, full_path, | 377 | |
| 273 | inode->i_sb, xid); | 378 | cifs_create_get_file_info: |
| 274 | else { | 379 | /* server might mask mode so we have to query for it */ |
| 275 | rc = cifs_get_inode_info(&newinode, full_path, | 380 | if (tcon->unix_ext) |
| 276 | buf, inode->i_sb, xid, | 381 | rc = cifs_get_inode_info_unix(&newinode, full_path, |
| 277 | &fileHandle); | 382 | inode->i_sb, xid); |
| 278 | if (newinode) { | 383 | else { |
| 279 | if (cifs_sb->mnt_cifs_flags & | 384 | rc = cifs_get_inode_info(&newinode, full_path, buf, |
| 280 | CIFS_MOUNT_DYNPERM) | 385 | inode->i_sb, xid, &fileHandle); |
| 281 | newinode->i_mode = mode; | 386 | if (newinode) { |
| 282 | if ((oplock & CIFS_CREATE_ACTION) && | 387 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) |
| 283 | (cifs_sb->mnt_cifs_flags & | 388 | newinode->i_mode = mode; |
| 284 | CIFS_MOUNT_SET_UID)) { | 389 | if ((oplock & CIFS_CREATE_ACTION) && |
| 285 | newinode->i_uid = current_fsuid(); | 390 | (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) { |
| 286 | if (inode->i_mode & S_ISGID) | 391 | newinode->i_uid = current_fsuid(); |
| 287 | newinode->i_gid = | 392 | if (inode->i_mode & S_ISGID) |
| 288 | inode->i_gid; | 393 | newinode->i_gid = inode->i_gid; |
| 289 | else | 394 | else |
| 290 | newinode->i_gid = | 395 | newinode->i_gid = current_fsgid(); |
| 291 | current_fsgid(); | ||
| 292 | } | ||
| 293 | } | 396 | } |
| 294 | } | 397 | } |
| 398 | } | ||
| 295 | 399 | ||
| 296 | if (rc != 0) { | 400 | cifs_create_set_dentry: |
| 297 | cFYI(1, ("Create worked, get_inode_info failed rc = %d", | 401 | if (rc == 0) |
| 298 | rc)); | 402 | setup_cifs_dentry(tcon, direntry, newinode); |
| 299 | } else | 403 | else |
| 300 | setup_cifs_dentry(tcon, direntry, newinode); | 404 | cFYI(1, ("Create worked, get_inode_info failed rc = %d", rc)); |
| 301 | 405 | ||
| 302 | if ((nd == NULL /* nfsd case - nfs srv does not set nd */) || | 406 | /* nfsd case - nfs srv does not set nd */ |
| 303 | (!(nd->flags & LOOKUP_OPEN))) { | 407 | if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) { |
| 304 | /* mknod case - do not leave file open */ | 408 | /* mknod case - do not leave file open */ |
| 305 | CIFSSMBClose(xid, tcon, fileHandle); | 409 | CIFSSMBClose(xid, tcon, fileHandle); |
| 306 | } else if (newinode) { | 410 | } else if (newinode) { |
| 307 | struct cifsFileInfo *pCifsFile = | 411 | struct cifsFileInfo *pCifsFile = |
| 308 | kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); | 412 | kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); |
| 309 | 413 | ||
| 310 | if (pCifsFile == NULL) | 414 | if (pCifsFile == NULL) |
| 311 | goto cifs_create_out; | 415 | goto cifs_create_out; |
| 312 | pCifsFile->netfid = fileHandle; | 416 | pCifsFile->netfid = fileHandle; |
| 313 | pCifsFile->pid = current->tgid; | 417 | pCifsFile->pid = current->tgid; |
| 314 | pCifsFile->pInode = newinode; | 418 | pCifsFile->pInode = newinode; |
| 315 | pCifsFile->invalidHandle = false; | 419 | pCifsFile->invalidHandle = false; |
| 316 | pCifsFile->closePend = false; | 420 | pCifsFile->closePend = false; |
| 317 | init_MUTEX(&pCifsFile->fh_sem); | 421 | init_MUTEX(&pCifsFile->fh_sem); |
| 318 | mutex_init(&pCifsFile->lock_mutex); | 422 | mutex_init(&pCifsFile->lock_mutex); |
| 319 | INIT_LIST_HEAD(&pCifsFile->llist); | 423 | INIT_LIST_HEAD(&pCifsFile->llist); |
| 320 | atomic_set(&pCifsFile->wrtPending, 0); | 424 | atomic_set(&pCifsFile->wrtPending, 0); |
| 321 | 425 | ||
| 322 | /* set the following in open now | 426 | /* set the following in open now |
| 323 | pCifsFile->pfile = file; */ | 427 | pCifsFile->pfile = file; */ |
| 324 | write_lock(&GlobalSMBSeslock); | 428 | write_lock(&GlobalSMBSeslock); |
| 325 | list_add(&pCifsFile->tlist, &tcon->openFileList); | 429 | list_add(&pCifsFile->tlist, &tcon->openFileList); |
| 326 | pCifsInode = CIFS_I(newinode); | 430 | pCifsInode = CIFS_I(newinode); |
| 327 | if (pCifsInode) { | 431 | if (pCifsInode) { |
| 328 | /* if readable file instance put first in list*/ | 432 | /* if readable file instance put first in list*/ |
| 329 | if (write_only) { | 433 | if (write_only) { |
| 330 | list_add_tail(&pCifsFile->flist, | 434 | list_add_tail(&pCifsFile->flist, |
| 331 | &pCifsInode->openFileList); | 435 | &pCifsInode->openFileList); |
| 332 | } else { | 436 | } else { |
| 333 | list_add(&pCifsFile->flist, | 437 | list_add(&pCifsFile->flist, |
| 334 | &pCifsInode->openFileList); | 438 | &pCifsInode->openFileList); |
| 335 | } | ||
| 336 | if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { | ||
| 337 | pCifsInode->clientCanCacheAll = true; | ||
| 338 | pCifsInode->clientCanCacheRead = true; | ||
| 339 | cFYI(1, ("Exclusive Oplock inode %p", | ||
| 340 | newinode)); | ||
| 341 | } else if ((oplock & 0xF) == OPLOCK_READ) | ||
| 342 | pCifsInode->clientCanCacheRead = true; | ||
| 343 | } | 439 | } |
| 344 | write_unlock(&GlobalSMBSeslock); | 440 | if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { |
| 441 | pCifsInode->clientCanCacheAll = true; | ||
| 442 | pCifsInode->clientCanCacheRead = true; | ||
| 443 | cFYI(1, ("Exclusive Oplock inode %p", | ||
| 444 | newinode)); | ||
| 445 | } else if ((oplock & 0xF) == OPLOCK_READ) | ||
| 446 | pCifsInode->clientCanCacheRead = true; | ||
| 345 | } | 447 | } |
| 448 | write_unlock(&GlobalSMBSeslock); | ||
| 346 | } | 449 | } |
| 347 | cifs_create_out: | 450 | cifs_create_out: |
| 348 | kfree(buf); | 451 | kfree(buf); |
