static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, struct tcon_link *tlink, unsigned oflags, umode_t mode, __u32 *oplock, struct cifs_fid *fid, int *created) { int rc = -ENOENT; int create_options = CREATE_NOT_DIR; int desired_access; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_tcon *tcon = tlink_tcon(tlink); char *full_path = NULL; FILE_ALL_INFO *buf = NULL; struct inode *newinode = NULL; int disposition; struct TCP_Server_Info *server = tcon->ses->server; *oplock = 0; if (tcon->ses->server->oplocks) *oplock = REQ_OPLOCK; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; goto out; } if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode, oflags, oplock, &fid->netfid, xid); switch (rc) { case 0: if (newinode == NULL) { /* query inode info */ goto cifs_create_get_file_info; } if (S_ISDIR(newinode->i_mode)) { CIFSSMBClose(xid, tcon, fid->netfid); iput(newinode); rc = -EISDIR; goto out; } if (!S_ISREG(newinode->i_mode)) { /* * The server may allow us to open things like * FIFOs, but the client isn't set up to deal * with that. If it's not a regular file, just * close it and proceed as if it were a normal * lookup. */ CIFSSMBClose(xid, tcon, fid->netfid); goto cifs_create_get_file_info; } /* success, no need to query */ goto cifs_create_set_dentry; case -ENOENT: goto cifs_create_get_file_info; case -EIO: case -EINVAL: /* * EIO could indicate that (posix open) operation is not * supported, despite what server claimed in capability * negotiation. * * POSIX open in samba versions 3.3.1 and earlier could * incorrectly fail with invalid parameter. */ tcon->broken_posix_open = true; break; case -EREMOTE: case -EOPNOTSUPP: /* * EREMOTE indicates DFS junction, which is not handled * in posix open. If either that or op not supported * returned, follow the normal lookup. */ break; default: goto out; } /* * fallthrough to retry, using older open call, this is case * where server does not support this SMB level, and falsely * claims capability (also get here for DFS case which should be * rare for path not covered on files) */ } desired_access = 0; if (OPEN_FMODE(oflags) & FMODE_READ) desired_access |= GENERIC_READ; /* is this too little? */ if (OPEN_FMODE(oflags) & FMODE_WRITE) desired_access |= GENERIC_WRITE; disposition = FILE_OVERWRITE_IF; if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) disposition = FILE_CREATE; else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) disposition = FILE_OVERWRITE_IF; else if ((oflags & O_CREAT) == O_CREAT) disposition = FILE_OPEN_IF; else cifs_dbg(FYI, "Create flag not set in create function\n"); /* * BB add processing to set equivalent of mode - e.g. via CreateX with * ACLs */ if (!server->ops->open) { rc = -ENOSYS; goto out; } buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (buf == NULL) { rc = -ENOMEM; goto out; } /* * if we're not using unix extensions, see if we need to set * ATTR_READONLY on the create call */ if (!tcon->unix_ext && (mode & S_IWUGO) == 0) create_options |= CREATE_OPTION_READONLY; if (backup_cred(cifs_sb)) create_options |= CREATE_OPEN_BACKUP_INTENT; rc = server->ops->open(xid, tcon, full_path, disposition, desired_access, create_options, fid, oplock, buf, cifs_sb); if (rc) { cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc); goto out; } /* * If Open reported that we actually created a file then we now have to * set the mode if possible. */ if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) { struct cifs_unix_set_info_args args = { .mode = mode, .ctime = NO_CHANGE_64, .atime = NO_CHANGE_64, .mtime = NO_CHANGE_64, .device = 0, }; *created |= FILE_CREATED; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { args.uid = current_fsuid(); if (inode->i_mode & S_ISGID) args.gid = inode->i_gid; else args.gid = current_fsgid(); } else { args.uid = INVALID_UID; /* no change */ args.gid = INVALID_GID; /* no change */ } CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, current->tgid); } else { /* * BB implement mode setting via Windows security * descriptors e.g. */ /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ /* Could set r/o dos attribute if mode & 0222 == 0 */ } cifs_create_get_file_info: /* server might mask mode so we have to query for it */ if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, xid); else { rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, &fid->netfid); if (newinode) { if (server->ops->set_lease_key) server->ops->set_lease_key(newinode, fid); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) newinode->i_mode = mode; if ((*oplock & CIFS_CREATE_ACTION) && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) { newinode->i_uid = current_fsuid(); if (inode->i_mode & S_ISGID) newinode->i_gid = inode->i_gid; else newinode->i_gid = current_fsgid(); } } } cifs_create_set_dentry: if (rc != 0) { cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", rc); goto out_err; } if (S_ISDIR(newinode->i_mode)) { rc = -EISDIR; goto out_err; } d_drop(direntry); d_add(direntry, newinode); out: kfree(buf); kfree(full_path); return rc; out_err: if (server->ops->close) server->ops->close(xid, tcon, fid); if (newinode) iput(newinode); goto out; }
void * cifs_follow_link(struct dentry *direntry, struct nameidata *nd) { struct inode *inode = direntry->d_inode; int rc = -ENOMEM; unsigned int xid; char *full_path = NULL; char *target_path = NULL; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct tcon_link *tlink = NULL; struct cifs_tcon *tcon; xid = get_xid(); tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) { rc = PTR_ERR(tlink); tlink = NULL; goto out; } tcon = tlink_tcon(tlink); /* * For now, we just handle symlinks with unix extensions enabled. * Eventually we should handle NTFS reparse points, and MacOS * symlink support. For instance... * * rc = CIFSSMBQueryReparseLinkInfo(...) * * For now, just return -EACCES when the server doesn't support posix * extensions. Note that we still allow querying symlinks when posix * extensions are manually disabled. We could disable these as well * but there doesn't seem to be any harm in allowing the client to * read them. */ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && !cap_unix(tcon->ses)) { rc = -EACCES; goto out; } full_path = build_path_from_dentry(direntry); if (!full_path) goto out; cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); rc = -EACCES; /* * First try Minshall+French Symlinks, if configured * and fallback to UNIX Extensions Symlinks. */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if ((rc != 0) && cap_unix(tcon->ses)) rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, cifs_sb->local_nls); kfree(full_path); out: if (rc != 0) { kfree(target_path); target_path = ERR_PTR(rc); } free_xid(xid); if (tlink) cifs_put_tlink(tlink); nd_set_link(nd, target_path); return NULL; }