+/****************************************************************************
+ Deal with SMB_SET_FILE_UNIX_BASIC.
+****************************************************************************/
+
+static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
+ const char *pdata,
+ int total_data,
+ files_struct *fsp,
+ const char *fname,
+ SMB_STRUCT_STAT *psbuf,
+ SMB_OFF_T *p_size,
+ struct utimbuf *p_tvs,
+ mode_t *p_unixmode,
+ int *p_dosmode)
+{
+ uid_t set_owner = (uid_t)SMB_UID_NO_CHANGE;
+ gid_t set_grp = (uid_t)SMB_GID_NO_CHANGE;
+ uint32 raw_unixmode;
+ BOOL delete_on_fail = False;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (total_data < 100) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ set_owner = VALID_STAT(*psbuf) ? psbuf->st_uid : (uid_t)SMB_UID_NO_CHANGE;
+ set_grp = VALID_STAT(*psbuf) ? psbuf->st_gid : (gid_t)SMB_GID_NO_CHANGE;
+
+ if(IVAL(pdata, 0) != SMB_SIZE_NO_CHANGE_LO &&
+ IVAL(pdata, 4) != SMB_SIZE_NO_CHANGE_HI) {
+ *p_size=IVAL(pdata,0); /* first 8 Bytes are size */
+#ifdef LARGE_SMB_OFF_T
+ *p_size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) {
+ /* more than 32 bits? */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+#endif /* LARGE_SMB_OFF_T */
+ }
+
+ pdata+=24; /* ctime & st_blocks are not changed */
+ p_tvs->actime = convert_timespec_to_time_t(interpret_long_date(pdata)); /* access_time */
+ p_tvs->modtime = convert_timespec_to_time_t(interpret_long_date(pdata+8)); /* modification_time */
+ pdata+=16;
+ set_owner = (uid_t)IVAL(pdata,0);
+ pdata += 8;
+ set_grp = (gid_t)IVAL(pdata,0);
+ pdata += 8;
+ raw_unixmode = IVAL(pdata,28);
+ *p_unixmode = unix_perms_from_wire(conn, psbuf, raw_unixmode);
+ *p_dosmode = 0; /* Ensure dos mode change doesn't override this. */
+
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC: name = %s \
+size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
+ fname, (double)*p_size, (unsigned int)set_owner, (unsigned int)set_grp, (int)raw_unixmode));
+
+ if (!VALID_STAT(*psbuf)) {
+
+ /*
+ * The only valid use of this is to create character and block
+ * devices, and named pipes. This is deprecated (IMHO) and
+ * a new info level should be used for mknod. JRA.
+ */
+
+ uint32 file_type = IVAL(pdata,0);
+#if defined(HAVE_MAKEDEV)
+ uint32 dev_major = IVAL(pdata,4);
+ uint32 dev_minor = IVAL(pdata,12);
+#endif
+
+ SMB_DEV_T dev = (SMB_DEV_T)0;
+
+ if (raw_unixmode == SMB_MODE_NO_CHANGE) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+#if defined(HAVE_MAKEDEV)
+ dev = makedev(dev_major, dev_minor);
+#endif
+
+ switch (file_type) {
+#if defined(S_IFIFO)
+ case UNIX_TYPE_FIFO:
+ *p_unixmode |= S_IFIFO;
+ break;
+#endif
+#if defined(S_IFSOCK)
+ case UNIX_TYPE_SOCKET:
+ *p_unixmode |= S_IFSOCK;
+ break;
+#endif
+#if defined(S_IFCHR)
+ case UNIX_TYPE_CHARDEV:
+ *p_unixmode |= S_IFCHR;
+ break;
+#endif
+#if defined(S_IFBLK)
+ case UNIX_TYPE_BLKDEV:
+ *p_unixmode |= S_IFBLK;
+ break;
+#endif
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \
+0%o for file %s\n", (double)dev, *p_unixmode, fname ));
+
+ /* Ok - do the mknod. */
+ if (SMB_VFS_MKNOD(conn, fname, *p_unixmode, dev) != 0) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ /* If any of the other "set" calls fail we
+ * don't want to end up with a half-constructed mknod.
+ */
+
+ delete_on_fail = True;
+
+ if (lp_inherit_perms(SNUM(conn))) {
+ inherit_access_acl(
+ conn, parent_dirname(fname),
+ fname, *p_unixmode);
+ }
+
+ if (SMB_VFS_STAT(conn, fname, psbuf) != 0) {
+ status = map_nt_error_from_unix(errno);
+ SMB_VFS_UNLINK(conn,fname);
+ return status;
+ }
+
+ /* Ensure we don't try and change anything else. */
+ raw_unixmode = SMB_MODE_NO_CHANGE;
+ *p_size = get_file_size(*psbuf);
+ p_tvs->modtime = psbuf->st_mtime;
+ p_tvs->actime = psbuf->st_atime;
+ }
+
+ /*
+ * Deal with the UNIX specific mode set.
+ */
+
+ if (raw_unixmode != SMB_MODE_NO_CHANGE) {
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC setting mode 0%o for file %s\n",
+ (unsigned int)*p_unixmode, fname ));
+ if (SMB_VFS_CHMOD(conn,fname,*p_unixmode) != 0) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ /*
+ * Deal with the UNIX specific uid set.
+ */
+
+ if ((set_owner != (uid_t)SMB_UID_NO_CHANGE) && (psbuf->st_uid != set_owner)) {
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC changing owner %u for file %s\n",
+ (unsigned int)set_owner, fname ));
+ if (SMB_VFS_CHOWN(conn, fname, set_owner, (gid_t)-1) != 0) {
+ status = map_nt_error_from_unix(errno);
+ if (delete_on_fail) {
+ SMB_VFS_UNLINK(conn,fname);
+ }
+ return status;
+ }
+ }
+
+ /*
+ * Deal with the UNIX specific gid set.
+ */
+
+ if ((set_grp != (uid_t)SMB_GID_NO_CHANGE) && (psbuf->st_gid != set_grp)) {
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC changing group %u for file %s\n",
+ (unsigned int)set_owner, fname ));
+ if (SMB_VFS_CHOWN(conn, fname, (uid_t)-1, set_grp) != 0) {
+ status = map_nt_error_from_unix(errno);
+ if (delete_on_fail) {
+ SMB_VFS_UNLINK(conn,fname);
+ }
+ return status;
+ }
+ }
+ return NT_STATUS_OK;
+}