int unlzw(in, out)
    int in, out;    /* input and output file descriptors */
{
    REG2   char_type  *stackp;
    REG3   code_int   code;
    REG4   int        finchar;
    REG5   code_int   oldcode;
    REG6   code_int   incode;
    REG7   long       inbits;
    REG8   long       posbits;
    REG9   int        outpos;
/*  REG10  int        insize; (global) */
    REG11  unsigned   bitmask;
    REG12  code_int   free_ent;
    REG13  code_int   maxcode;
    REG14  code_int   maxmaxcode;
    REG15  int        n_bits;
    REG16  int        rsize;

#ifdef MAXSEG_64K
    tab_prefix[0] = tab_prefix0;
    tab_prefix[1] = tab_prefix1;
#endif
    maxbits = get_byte();
    block_mode = maxbits & BLOCK_MODE;
    if ((maxbits & LZW_RESERVED) != 0) {
	WARN((stderr, "\n%s: %s: warning, unknown flags 0x%x\n",
	      program_name, ifname, maxbits & LZW_RESERVED));
    }
    maxbits &= BIT_MASK;
    maxmaxcode = MAXCODE(maxbits);

    if (maxbits > BITS) {
	fprintf(stderr,
		"\n%s: %s: compressed with %d bits, can only handle %d bits\n",
		program_name, ifname, maxbits, BITS);
	exit_code = ERROR;
	return ERROR;
    }
    rsize = insize;
    maxcode = MAXCODE(n_bits = INIT_BITS)-1;
    bitmask = (1<<n_bits)-1;
    oldcode = -1;
    finchar = 0;
    outpos = 0;
    posbits = inptr<<3;

    free_ent = ((block_mode) ? FIRST : 256);

    clear_tab_prefixof(); /* Initialize the first 256 entries in the table. */

    for (code = 255 ; code >= 0 ; --code) {
	tab_suffixof(code) = (char_type)code;
    }
    do {
	REG1 int i;
	int  e;
	int  o;

    resetbuf:
	o = posbits >> 3;
	e = o <= insize ? insize - o : 0;

	for (i = 0 ; i < e ; ++i) {
	    inbuf[i] = inbuf[i+o];
	}
	insize = e;
	posbits = 0;

	if (insize < INBUF_EXTRA) {
	    rsize = read_buffer (in, (char *) inbuf + insize, INBUFSIZ);
	    if (rsize == -1) {
		read_error();
	    }
	    insize += rsize;
	    bytes_in += (off_t)rsize;
	}
	inbits = ((rsize != 0) ? ((long)insize - insize%n_bits)<<3 :
		  ((long)insize<<3)-(n_bits-1));

	while (inbits > posbits) {
	    if (free_ent > maxcode) {
		posbits = ((posbits-1) +
			   ((n_bits<<3)-(posbits-1+(n_bits<<3))%(n_bits<<3)));
		++n_bits;
		if (n_bits == maxbits) {
		    maxcode = maxmaxcode;
		} else {
		    maxcode = MAXCODE(n_bits)-1;
		}
		bitmask = (1<<n_bits)-1;
		goto resetbuf;
	    }
	    input(inbuf,posbits,code,n_bits,bitmask);
	    Tracev((stderr, "%d ", code));

	    if (oldcode == -1) {
		if (256 <= code)
		  gzip_error ("corrupt input.");
		outbuf[outpos++] = (char_type)(finchar = (int)(oldcode=code));
		continue;
	    }
	    if (code == CLEAR && block_mode) {
		clear_tab_prefixof();
		free_ent = FIRST - 1;
		posbits = ((posbits-1) +
			   ((n_bits<<3)-(posbits-1+(n_bits<<3))%(n_bits<<3)));
		maxcode = MAXCODE(n_bits = INIT_BITS)-1;
		bitmask = (1<<n_bits)-1;
		goto resetbuf;
	    }
	    incode = code;
	    stackp = de_stack;

	    if (code >= free_ent) { /* Special case for KwKwK string. */
		if (code > free_ent) {
#ifdef DEBUG
		    char_type *p;

		    posbits -= n_bits;
		    p = &inbuf[posbits>>3];
		    fprintf(stderr,
			    "code:%ld free_ent:%ld n_bits:%d insize:%u\n",
			    code, free_ent, n_bits, insize);
		    fprintf(stderr,
			    "posbits:%ld inbuf:%02X %02X %02X %02X %02X\n",
			    posbits, p[-1],p[0],p[1],p[2],p[3]);
#endif
		    if (!test && outpos > 0) {
			write_buf(out, (char*)outbuf, outpos);
			bytes_out += (off_t)outpos;
		    }
		    gzip_error (to_stdout
				? "corrupt input."
				: "corrupt input. Use zcat to recover some data.");
		}
		*--stackp = (char_type)finchar;
		code = oldcode;
	    }

	    while ((cmp_code_int)code >= (cmp_code_int)256) {
		/* Generate output characters in reverse order */
		*--stackp = tab_suffixof(code);
		code = tab_prefixof(code);
	    }
	    *--stackp =	(char_type)(finchar = tab_suffixof(code));

	    /* And put them out in forward order */
	    {
		REG1 int	i;

		if (outpos+(i = (de_stack-stackp)) >= OUTBUFSIZ) {
		    do {
			if (i > OUTBUFSIZ-outpos) i = OUTBUFSIZ-outpos;

			if (i > 0) {
			    memcpy(outbuf+outpos, stackp, i);
			    outpos += i;
			}
			if (outpos >= OUTBUFSIZ) {
			    if (!test) {
				write_buf(out, (char*)outbuf, outpos);
				bytes_out += (off_t)outpos;
			    }
			    outpos = 0;
			}
			stackp+= i;
		    } while ((i = (de_stack-stackp)) > 0);
		} else {
		    memcpy(outbuf+outpos, stackp, i);
		    outpos += i;
		}
	    }

	    if ((code = free_ent) < maxmaxcode) { /* Generate the new entry. */

		tab_prefixof(code) = (unsigned short)oldcode;
		tab_suffixof(code) = (char_type)finchar;
		free_ent = code+1;
	    }
	    oldcode = incode;	/* Remember previous code.	*/
	}
    } while (rsize != 0);

    if (!test && outpos > 0) {
	write_buf(out, (char*)outbuf, outpos);
	bytes_out += (off_t)outpos;
    }
    return OK;
}


_get_gsub (hb_face_t *face)
{
  return HB_LIKELY (face->ot_layout.gsub) ? *face->ot_layout.gsub : Null(GSUB);
}


_hb_ot_layout_get_glyph_property (hb_face_t      *face,
				  hb_codepoint_t  glyph)
{
  hb_ot_layout_class_t klass;
  const GDEF &gdef = _get_gdef (face);

  klass = gdef.get_glyph_class (glyph);

  if (!klass && glyph < face->ot_layout.new_gdef.len)
    klass = face->ot_layout.new_gdef.klasses[glyph];

  switch (klass) {
  default:
  case GDEF::UnclassifiedGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
  case GDEF::BaseGlyph:		return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
  case GDEF::LigatureGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
  case GDEF::ComponentGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
  case GDEF::MarkGlyph:
	klass = gdef.get_mark_attachment_type (glyph);
	return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8);
  }
}


hb_ot_layout_script_find_language (hb_face_t    *face,
				   hb_tag_t      table_tag,
				   unsigned int  script_index,
				   hb_tag_t      language_tag,
				   unsigned int *language_index)
{
  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);

  if (s.find_lang_sys_index (language_tag, language_index))
    return TRUE;

  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
  if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index))
    return FALSE;

  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
  return FALSE;
}


static int is_halted(USBHostDevice *s, int ep)
{
    return s->endp_table[ep - 1].halted;
}


static void async_complete(void *opaque)
{
    USBHostDevice *s = opaque;
    AsyncURB *aurb;

    while (1) {
    	USBPacket *p;

	int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
        if (r < 0) {
            if (errno == EAGAIN)
                return;

            if (errno == ENODEV && !s->closing) {
                printf("husb: device %d.%d disconnected\n", s->bus_num, s->addr);
	        usb_device_del_addr(0, s->dev.addr);
                return;
            }

            dprintf("husb: async. reap urb failed errno %d\n", errno);
            return;
        }

        p = aurb->packet;

	dprintf("husb: async completed. aurb %p status %d alen %d\n",
                aurb, aurb->urb.status, aurb->urb.actual_length);

	if (p) {
            switch (aurb->urb.status) {
            case 0:
                p->len = aurb->urb.actual_length;
                if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL)
                    async_complete_ctrl(s, p);
                break;

            case -EPIPE:
                set_halt(s, p->devep);
                /* fall through */
            default:
                p->len = USB_RET_NAK;
                break;
            }

            usb_packet_complete(p);
	}

        async_free(aurb);
    }
}


static void clear_halt(USBHostDevice *s, int ep)
{
    s->endp_table[ep - 1].halted = 0;
}


static USBHostDevice *hostdev_find(int bus_num, int addr)
{
    USBHostDevice *s = hostdev_list;
    while (s) {
        if (s->bus_num == bus_num && s->addr == addr)
            return s;
        s = s->next;
    }
    return NULL;
}


gs_manager_set_keyboard_enabled (GSManager *manager,
                                 gboolean   enabled)
{
        g_return_if_fail (GS_IS_MANAGER (manager));

        if (manager->priv->keyboard_enabled != enabled) {
                GSList *l;

                manager->priv->keyboard_enabled = enabled;
                for (l = manager->priv->windows; l; l = l->next) {
                        gs_window_set_keyboard_enabled (l->data, enabled);
                }
        }
}


gs_manager_cancel_unlock_request (GSManager *manager)
{
        GSList *l;
        for (l = manager->priv->windows; l; l = l->next) {
                gs_window_cancel_unlock_request (l->data);
        }
}


window_dialog_up_cb (GSWindow  *window,
                     GSManager *manager)
{
        GSList *l;

        g_return_if_fail (manager != NULL);
        g_return_if_fail (GS_IS_MANAGER (manager));

        gs_debug ("Handling dialog up");

        g_signal_emit (manager, signals [AUTH_REQUEST_BEGIN], 0);

        manager->priv->dialog_up = TRUE;
        /* Make all other windows insensitive so we don't get events */
        for (l = manager->priv->windows; l; l = l->next) {
                if (l->data != window) {
                        gtk_widget_set_sensitive (GTK_WIDGET (l->data), FALSE);
                }
        }

        /* Move keyboard and mouse grabs so dialog can be used */
        gs_grab_move_to_window (manager->priv->grab,
                                gs_window_get_gdk_window (window),
                                gs_window_get_screen (window),
                                FALSE);

        /* Release the pointer grab while dialog is up so that
           the dialog can be used.  We'll regrab it when the dialog goes down. */
        gs_grab_release_mouse (manager->priv->grab);

        if (! manager->priv->throttled) {
                gs_debug ("Suspending jobs");

                manager_suspend_jobs (manager);
        }
}


window_dialog_down_cb (GSWindow  *window,
                       GSManager *manager)
{
        GSList *l;

        g_return_if_fail (manager != NULL);
        g_return_if_fail (GS_IS_MANAGER (manager));

        gs_debug ("Handling dialog down");

        /* Regrab the mouse */
        gs_grab_move_to_window (manager->priv->grab,
                                gs_window_get_gdk_window (window),
                                gs_window_get_screen (window),
                                FALSE);

        /* Make all windows sensitive so we get events */
        for (l = manager->priv->windows; l; l = l->next) {
                gtk_widget_set_sensitive (GTK_WIDGET (l->data), TRUE);
        }

        manager->priv->dialog_up = FALSE;

        if (! manager->priv->throttled) {
                manager_resume_jobs (manager);
        }

        g_signal_emit (manager, signals [AUTH_REQUEST_END], 0);
}


cycle_timeout (GSManager *manager)
{
        g_return_val_if_fail (manager != NULL, FALSE);
        g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);

        if (! manager->priv->dialog_up) {
                gs_manager_cycle (manager);
        }

        return TRUE;
}


gs_manager_set_keyboard_command (GSManager  *manager,
                                 const char *command)
{
        GSList *l;

        g_return_if_fail (GS_IS_MANAGER (manager));

        g_free (manager->priv->keyboard_command);

        if (command) {
                manager->priv->keyboard_command = g_strdup (command);
        } else {
                manager->priv->keyboard_command = NULL;
        }

        for (l = manager->priv->windows; l; l = l->next) {
                gs_window_set_keyboard_command (l->data, manager->priv->keyboard_command);
        }
}


manager_maybe_grab_window (GSManager *manager,
                           GSWindow  *window)
{
        GdkDisplay *display;
        GdkScreen  *screen;
        int         monitor;
        int         x, y;
        gboolean    grabbed;

        display = gdk_display_get_default ();
        gdk_display_get_pointer (display, &screen, &x, &y, NULL);
        monitor = gdk_screen_get_monitor_at_point (screen, x, y);

        gdk_flush ();
        grabbed = FALSE;
        if (gs_window_get_screen (window) == screen
            && gs_window_get_monitor (window) == monitor) {
                gs_debug ("Moving grab to %p", window);
                gs_grab_move_to_window (manager->priv->grab,
                                        gs_window_get_gdk_window (window),
                                        gs_window_get_screen (window),
                                        FALSE);
                grabbed = TRUE;
        }

        return grabbed;
}


watch_bg_preferences (GSManager *manager)
{
        g_assert (manager->priv->bg_notify_id == 0);

        gconf_client_add_dir (manager->priv->client,
                              GNOME_BG_KEY_DIR,
                              GCONF_CLIENT_PRELOAD_NONE,
                              NULL);
        manager->priv->bg_notify_id = gconf_client_notify_add (manager->priv->client,
                                                               GNOME_BG_KEY_DIR,
                                                               (GConfClientNotifyFunc)gconf_changed_callback,
                                                               manager,
                                                               NULL,
                                                               NULL);
}


window_deactivated_cb (GSWindow  *window,
                       GSManager *manager)
{
        g_return_if_fail (manager != NULL);
        g_return_if_fail (GS_IS_MANAGER (manager));

        g_idle_add ((GSourceFunc)window_deactivated_idle, manager);
}


gs_manager_cycle (GSManager *manager)
{
        g_return_val_if_fail (manager != NULL, FALSE);
        g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);

        gs_debug ("cycling jobs");

        if (! manager->priv->active) {
                return FALSE;
        }

        if (manager->priv->dialog_up) {
                return FALSE;
        }

        if (manager->priv->throttled) {
                return FALSE;
        }

        manager_cycle_jobs (manager);

        return TRUE;
}


gs_manager_set_away_message (GSManager  *manager,
                             const char *away_message)
{
        GSList *l;

        g_return_if_fail (GS_IS_MANAGER (manager));

        g_free (manager->priv->away_message);

        manager->priv->away_message = g_strdup (away_message);

        for (l = manager->priv->windows; l; l = l->next) {
                gs_window_set_away_message (l->data, manager->priv->away_message);
        }
}


gs_manager_get_property (GObject            *object,
                         guint               prop_id,
                         GValue             *value,
                         GParamSpec         *pspec)
{
        GSManager *self;

        self = GS_MANAGER (object);

        switch (prop_id) {
        case PROP_THROTTLED:
                g_value_set_boolean (value, self->priv->throttled);
                break;
        case PROP_LOCK_ENABLED:
                g_value_set_boolean (value, self->priv->lock_enabled);
                break;
        case PROP_LOCK_TIMEOUT:
                g_value_set_long (value, self->priv->lock_timeout);
                break;
        case PROP_LOGOUT_ENABLED:
                g_value_set_boolean (value, self->priv->logout_enabled);
                break;
        case PROP_KEYBOARD_ENABLED:
                g_value_set_boolean (value, self->priv->keyboard_enabled);
                break;
        case PROP_USER_SWITCH_ENABLED:
                g_value_set_boolean (value, self->priv->user_switch_enabled);
                break;
        case PROP_LOGOUT_TIMEOUT:
                g_value_set_long (value, self->priv->logout_timeout);
                break;
        case PROP_LOGOUT_COMMAND:
                g_value_set_string (value, self->priv->logout_command);
                break;
        case PROP_KEYBOARD_COMMAND:
                g_value_set_string (value, self->priv->keyboard_command);
                break;
        case PROP_AWAY_MESSAGE:
                g_value_set_string (value, self->priv->away_message);
                break;
        case PROP_CYCLE_TIMEOUT:
                g_value_set_long (value, self->priv->cycle_timeout);
                break;
        case PROP_ACTIVE:
                g_value_set_boolean (value, self->priv->active);
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}


on_randr_event (GnomeRRScreen *screen, gpointer data)
{
        GsdXrandrManager *manager = GSD_XRANDR_MANAGER (data);
        GsdXrandrManagerPrivate *priv = manager->priv;
        guint32 change_timestamp, config_timestamp;

        if (!priv->running)
                return;

        gnome_rr_screen_get_timestamps (screen, &change_timestamp, &config_timestamp);

        if (change_timestamp > config_timestamp) {
                /* The event is due to an explicit configuration change.
                 *
                 * If the change was performed by us, then we need to do nothing.
                 *
                 * If the change was done by some other X client, we don't need
                 * to do anything, either; the screen is already configured.
                 */
                show_timestamps_dialog (manager, "ignoring since change > config");
        } else {
                /* Here, config_timestamp >= change_timestamp.  This means that
                 * the screen got reconfigured because of hotplug/unplug; the X
                 * server is just notifying us, and we need to configure the
                 * outputs in a sane way.
                 */
#if 1
                auto_configure_outputs (manager, config_timestamp);
#else
                /* WHY THIS CODE IS DISABLED:
                 *
                 * The strategy of "on hotplug or unsuspend, restore a
                 * known-good configuration, and fall back to autoconfiguration"
                 * works fine as long as you don't happen to be running
                 * gnome-display-properties and click its "Detect displays"
                 * button.
                 *
                 * If you do that, the RANDR calls from g-d-p will cause the X
                 * server to re-probe the RANDR outputs.  The server will send
                 * us an event, we'll restore the configuration to something
                 * else... and you'll be weirded out, because "just detecting
                 * your monitors" should not change the current configuration,
                 * right?
                 *
                 * We may need some kind of D-bus API so that g-d-p can inhibit
                 * this RANDR plugin's reconfiguration-fu when the "Detect
                 * displays" button is being used.
                 */

                char *intended_filename;
                GError *error;
                gboolean success;

                show_timestamps_dialog (manager, "need to deal with reconfiguration, as config >= change");

                intended_filename = gnome_rr_config_get_intended_filename ();

                error = NULL;
                success = apply_configuration_from_filename (manager, intended_filename, TRUE, config_timestamp, &error);
                g_free (intended_filename);

                if (!success) {
                        /* We don't bother checking the error type.
                         *
                         * Both G_FILE_ERROR_NOENT and
                         * GNOME_RR_ERROR_NO_MATCHING_CONFIG would mean, "there
                         * was no configuration to apply, or none that matched
                         * the current outputs", and in that case we need to run
                         * our fallback.
                         *
                         * Any other error means "we couldn't do the smart thing
                         * of using a previously- saved configuration, anyway,
                         * for some other reason.  In that case, we also need to
                         * run our fallback to avoid leaving the user with a
                         * bogus configuration.
                         */

                        if (error)
                                g_error_free (error);

                        auto_configure_outputs (manager, config_timestamp);
                }
#endif
        }

        refresh_tray_icon_menu_if_active (manager, MAX (change_timestamp, config_timestamp));
}


apply_configuration_from_filename (GsdXrandrManager *manager,
                                   const char       *filename,
                                   gboolean          no_matching_config_is_an_error,
                                   guint32           timestamp,
                                   GError          **error)
{
        struct GsdXrandrManagerPrivate *priv = manager->priv;
        GError *my_error;
        gboolean success;
        char *str;

        str = g_strdup_printf ("Applying %s with timestamp %d", filename, timestamp);
        show_timestamps_dialog (manager, str);
        g_free (str);

        my_error = NULL;
        success = gnome_rr_config_apply_from_filename_with_time (priv->rw_screen, filename, timestamp, &my_error);
        if (success)
                return TRUE;

        if (g_error_matches (my_error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_MATCHING_CONFIG)) {
                if (no_matching_config_is_an_error)
                        goto fail;

                /* This is not an error; the user probably changed his monitors
                 * and so they don't match any of the stored configurations.
                 */
                g_error_free (my_error);
                return TRUE;
        }

fail:
        g_propagate_error (error, my_error);
        return FALSE;
}


gsd_xrandr_manager_apply_configuration (GsdXrandrManager *manager,
                                        GError          **error)
{
        return try_to_apply_intended_configuration (manager, NULL, GDK_CURRENT_TIME, error);
}


apply_stored_configuration_at_startup (GsdXrandrManager *manager, guint32 timestamp)
{
        GError *my_error;
        gboolean success;
        char *backup_filename;
        char *intended_filename;

        backup_filename = gnome_rr_config_get_backup_filename ();
        intended_filename = gnome_rr_config_get_intended_filename ();

        /* 1. See if there was a "saved" configuration.  If there is one, it means
         * that the user had selected to change the display configuration, but the
         * machine crashed.  In that case, we'll apply *that* configuration and save it on top of the
         * "intended" one.
         */

        my_error = NULL;

        success = apply_configuration_from_filename (manager, backup_filename, FALSE, timestamp, &my_error);
        if (success) {
                /* The backup configuration existed, and could be applied
                 * successfully, so we must restore it on top of the
                 * failed/intended one.
                 */
                restore_backup_configuration (manager, backup_filename, intended_filename, timestamp);
                goto out;
        }

        if (!g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
                /* Epic fail:  there (probably) was a backup configuration, but
                 * we could not apply it.  The only thing we can do is delete
                 * the backup configuration.  Let's hope that the user doesn't
                 * get left with an unusable display...
                 */

                unlink (backup_filename);
                goto out;
        }

        /* 2. There was no backup configuration!  This means we are
         * good.  Apply the intended configuration instead.
         */

        apply_intended_configuration (manager, intended_filename, timestamp);

out:

        if (my_error)
                g_error_free (my_error);

        g_free (backup_filename);
        g_free (intended_filename);
}


popup_menu_configure_display_cb (GtkMenuItem *item, gpointer data)
{
        GdkScreen *screen;
        GError *error;

        screen = gtk_widget_get_screen (GTK_WIDGET (item));

        error = NULL;
        if (!gdk_spawn_command_line_on_screen (screen, GSD_XRANDR_DISPLAY_CAPPLET, &error)) {
		GtkWidget *dialog;

		dialog = gtk_message_dialog_new_with_markup (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                                                             ""
                                                             "Display configuration could not be run"
                                                             "\n\n"
                                                             "%s", error->message);
		gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);

		g_error_free (error);
        }
}


get_best_visual_for_screen (GdkScreen *screen)
{
        char         *command;
        char         *std_output;
        int           exit_status;
        GError       *error;
        unsigned long v;
        char          c;
        GdkVisual    *visual;
        gboolean      res;

        visual = NULL;

        command = g_build_filename (LIBEXECDIR, "gnome-screensaver-gl-helper", NULL);

        error = NULL;
        std_output = NULL;
        res = spawn_command_line_on_screen_sync (screen,
                                                 command,
                                                 &std_output,
                                                 NULL,
                                                 &exit_status,
                                                 &error);
        if (! res) {
                gs_debug ("Could not run command '%s': %s", command, error->message);
                g_error_free (error);
                goto out;
        }

        if (1 == sscanf (std_output, "0x%lx %c", &v, &c)) {
                if (v != 0) {
                        VisualID      visual_id;

                        visual_id = (VisualID) v;
                        visual = gdkx_visual_get (visual_id);

                        gs_debug ("Found best GL visual for screen %d: 0x%x",
                                  gdk_screen_get_number (screen),
                                  (unsigned int) visual_id);
                }
        }
 out:
        g_free (std_output);
        g_free (command);

        return visual;
}


spawn_on_window (GSWindow *window,
                 char     *command,
                 int      *pid,
                 GIOFunc   watch_func,
                 gpointer  user_data,
                 gint     *watch_id)
{
        int         argc;
        char      **argv;
        GError     *error;
        gboolean    result;
        GIOChannel *channel;
        int         standard_output;
        int         standard_error;
        int         child_pid;
        int         id;

        error = NULL;
        if (! g_shell_parse_argv (command, &argc, &argv, &error)) {
                gs_debug ("Could not parse command: %s", error->message);
                g_error_free (error);
                return FALSE;
        }

        error = NULL;
        result = gdk_spawn_on_screen_with_pipes (GTK_WINDOW (window)->screen,
                                                 NULL,
                                                 argv,
                                                 NULL,
                                                 G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
                                                 NULL,
                                                 NULL,
                                                 &child_pid,
                                                 NULL,
                                                 &standard_output,
                                                 &standard_error,
                                                 &error);

        if (! result) {
                gs_debug ("Could not start command '%s': %s", command, error->message);
                g_error_free (error);
                g_strfreev (argv);
                return FALSE;
        }

        if (pid != NULL) {
                *pid = child_pid;
        } else {
                g_spawn_close_pid (child_pid);
        }

        /* output channel */
        channel = g_io_channel_unix_new (standard_output);
        g_io_channel_set_close_on_unref (channel, TRUE);
        g_io_channel_set_flags (channel,
                                g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
                                NULL);
        id = g_io_add_watch (channel,
                             G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
                             watch_func,
                             user_data);
        if (watch_id != NULL) {
                *watch_id = id;
        }
        g_io_channel_unref (channel);

        /* error channel */
        channel = g_io_channel_unix_new (standard_error);
        g_io_channel_set_close_on_unref (channel, TRUE);
        g_io_channel_set_flags (channel,
                                g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
                                NULL);
        id = g_io_add_watch (channel,
                             G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
                             error_watch,
                             NULL);
        g_io_channel_unref (channel);

        g_strfreev (argv);

        return result;
}


kill_keyboard_command (GSWindow *window)
{
        if (window->priv->keyboard_pid > 0) {
                signal_pid (window->priv->keyboard_pid, SIGTERM);
        }
}


gs_window_set_monitor (GSWindow *window,
                       int       monitor)
{
        g_return_if_fail (GS_IS_WINDOW (window));

        if (window->priv->monitor == monitor) {
                return;
        }

        window->priv->monitor = monitor;

        gtk_widget_queue_resize (GTK_WIDGET (window));

        g_object_notify (G_OBJECT (window), "monitor");
}


gs_window_set_keyboard_enabled (GSWindow *window,
                                gboolean  enabled)
{
        g_return_if_fail (GS_IS_WINDOW (window));

        window->priv->keyboard_enabled = enabled;
}


lock_socket_destroyed (GtkWidget *widget,
                       GSWindow  *window)
{
        g_signal_handlers_disconnect_by_func (widget, lock_socket_show, window);
        g_signal_handlers_disconnect_by_func (widget, lock_socket_destroyed, window);
        g_signal_handlers_disconnect_by_func (widget, lock_plug_added, window);
        g_signal_handlers_disconnect_by_func (widget, lock_plug_removed, window);

        window->priv->lock_socket = NULL;
}


create_lock_socket (GSWindow *window,
                    guint32   id)
{
        window->priv->lock_socket = gtk_socket_new ();
        window->priv->lock_box = gtk_alignment_new (0.5, 0.5, 0, 0);
        gtk_widget_show (window->priv->lock_box);
        gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->lock_box, TRUE, TRUE, 0);

        gtk_container_add (GTK_CONTAINER (window->priv->lock_box), window->priv->lock_socket);

        g_signal_connect (window->priv->lock_socket, "show",
                          G_CALLBACK (lock_socket_show), window);
        g_signal_connect (window->priv->lock_socket, "destroy",
                          G_CALLBACK (lock_socket_destroyed), window);
        g_signal_connect (window->priv->lock_socket, "plug_added",
                          G_CALLBACK (lock_plug_added), window);
        g_signal_connect (window->priv->lock_socket, "plug_removed",
                          G_CALLBACK (lock_plug_removed), window);

        gtk_socket_add_id (GTK_SOCKET (window->priv->lock_socket), id);

        if (window->priv->keyboard_enabled) {
                embed_keyboard (window);
        }
}


get_outside_region (GSWindow *window)
{
        int        i;
        GdkRegion *region;

        region = gdk_region_new ();
        for (i = 0; i < window->priv->monitor; i++) {
                GdkRectangle geometry;

                gdk_screen_get_monitor_geometry (GTK_WINDOW (window)->screen,
                                                   i, &geometry);
                gdk_region_union_with_rect (region, &geometry);
        }

        return region;
}


info_bar_timeout (GSWindow *window)
{
        window->priv->info_bar_timer_id = 0;
        gtk_widget_hide (window->priv->info_bar);
        return FALSE;
}


ssize_t smb_vfs_call_write(struct vfs_handle_struct *handle,
			   struct files_struct *fsp, const void *data,
			   size_t n)
{
	VFS_FIND(write);
	return handle->fns->write(handle, fsp, data, n);
}


const char *smb_vfs_call_connectpath(struct vfs_handle_struct *handle,
				     const char *filename)
{
	VFS_FIND(connectpath);
	return handle->fns->connectpath(handle, filename);
}


static bool smbd_marshall_dir_entry(TALLOC_CTX *ctx,
				    connection_struct *conn,
				    uint16_t flags2,
				    uint32_t info_level,
				    struct ea_list *name_list,
				    bool check_mangled_names,
				    bool requires_resume_key,
				    uint32_t mode,
				    const char *fname,
				    const struct smb_filename *smb_fname,
				    uint64_t space_remaining,
				    uint8_t align,
				    bool do_pad,
				    char *base_data,
				    char **ppdata,
				    char *end_data,
				    bool *out_of_space,
				    uint64_t *last_entry_off)
{
	char *p, *q, *pdata = *ppdata;
	uint32_t reskey=0;
	uint64_t file_size = 0;
	uint64_t allocation_size = 0;
	uint32_t len;
	struct timespec mdate_ts, adate_ts, cdate_ts, create_date_ts;
	time_t mdate = (time_t)0, adate = (time_t)0, create_date = (time_t)0;
	time_t c_date = (time_t)0;
	char *nameptr;
	char *last_entry_ptr;
	bool was_8_3;
	off_t off;
	off_t pad = 0;

	*out_of_space = false;

	ZERO_STRUCT(mdate_ts);
	ZERO_STRUCT(adate_ts);
	ZERO_STRUCT(create_date_ts);
	ZERO_STRUCT(cdate_ts);

	if (!(mode & aDIR)) {
		file_size = get_file_size_stat(&smb_fname->st);
	}
	allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st);

	mdate_ts = smb_fname->st.st_ex_mtime;
	adate_ts = smb_fname->st.st_ex_atime;
	create_date_ts = get_create_timespec(conn, NULL, smb_fname);
	cdate_ts = get_change_timespec(conn, NULL, smb_fname);

	if (lp_dos_filetime_resolution(SNUM(conn))) {
		dos_filetime_timespec(&create_date_ts);
		dos_filetime_timespec(&mdate_ts);
		dos_filetime_timespec(&adate_ts);
		dos_filetime_timespec(&cdate_ts);
	}

	create_date = convert_timespec_to_time_t(create_date_ts);
	mdate = convert_timespec_to_time_t(mdate_ts);
	adate = convert_timespec_to_time_t(adate_ts);
	c_date = convert_timespec_to_time_t(cdate_ts);

	/* align the record */
	off = PTR_DIFF(pdata, base_data);
	pad = (off + (align-1)) & ~(align-1);
	pad -= off;
	off += pad;
	/* initialize padding to 0 */
	if (pad) {
		memset(pdata, 0, pad);
	}
	space_remaining -= pad;

	pdata += pad;
	p = pdata;
	last_entry_ptr = p;

	pad = 0;
	off = 0;

	switch (info_level) {
	case SMB_FIND_INFO_STANDARD:
		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_INFO_STANDARD\n"));
		if(requires_resume_key) {
			SIVAL(p,0,reskey);
			p += 4;
		}
		srv_put_dos_date2(p,0,create_date);
		srv_put_dos_date2(p,4,adate);
		srv_put_dos_date2(p,8,mdate);
		SIVAL(p,12,(uint32)file_size);
		SIVAL(p,16,(uint32)allocation_size);
		SSVAL(p,20,mode);
		p += 23;
		nameptr = p;
		if (flags2 & FLAGS2_UNICODE_STRINGS) {
			p += ucs2_align(base_data, p, 0);
		}
		len = srvstr_push(base_data, flags2, p,
				  fname, PTR_DIFF(end_data, p),
				  STR_TERMINATE);
		if (flags2 & FLAGS2_UNICODE_STRINGS) {
			if (len > 2) {
				SCVAL(nameptr, -1, len - 2);
			} else {
				SCVAL(nameptr, -1, 0);
			}
		} else {
			if (len > 1) {
				SCVAL(nameptr, -1, len - 1);
			} else {
				SCVAL(nameptr, -1, 0);
			}
		}
		p += len;
		break;

	case SMB_FIND_EA_SIZE:
		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_EA_SIZE\n"));
		if (requires_resume_key) {
			SIVAL(p,0,reskey);
			p += 4;
		}
		srv_put_dos_date2(p,0,create_date);
		srv_put_dos_date2(p,4,adate);
		srv_put_dos_date2(p,8,mdate);
		SIVAL(p,12,(uint32)file_size);
		SIVAL(p,16,(uint32)allocation_size);
		SSVAL(p,20,mode);
		{
			unsigned int ea_size = estimate_ea_size(conn, NULL,
								smb_fname->base_name);
			SIVAL(p,22,ea_size); /* Extended attributes */
		}
		p += 27;
		nameptr = p - 1;
		len = srvstr_push(base_data, flags2,
				  p, fname, PTR_DIFF(end_data, p),
				  STR_TERMINATE | STR_NOALIGN);
		if (flags2 & FLAGS2_UNICODE_STRINGS) {
			if (len > 2) {
				len -= 2;
			} else {
				len = 0;
			}
		} else {
			if (len > 1) {
				len -= 1;
			} else {
				len = 0;
			}
		}
		SCVAL(nameptr,0,len);
		p += len;
		SCVAL(p,0,0); p += 1; /* Extra zero byte ? - why.. */
		break;

	case SMB_FIND_EA_LIST:
	{
		struct ea_list *file_list = NULL;
		size_t ea_len = 0;

		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_EA_LIST\n"));
		if (!name_list) {
			return false;
		}
		if (requires_resume_key) {
			SIVAL(p,0,reskey);
			p += 4;
		}
		srv_put_dos_date2(p,0,create_date);
		srv_put_dos_date2(p,4,adate);
		srv_put_dos_date2(p,8,mdate);
		SIVAL(p,12,(uint32)file_size);
		SIVAL(p,16,(uint32)allocation_size);
		SSVAL(p,20,mode);
		p += 22; /* p now points to the EA area. */

		file_list = get_ea_list_from_file(ctx, conn, NULL,
						  smb_fname->base_name,
						  &ea_len);
		name_list = ea_list_union(name_list, file_list, &ea_len);

		/* We need to determine if this entry will fit in the space available. */
		/* Max string size is 255 bytes. */
		if (PTR_DIFF(p + 255 + ea_len,pdata) > space_remaining) {
			*out_of_space = true;
			DEBUG(9,("smbd_marshall_dir_entry: out of space\n"));
			return False; /* Not finished - just out of space */
		}

		/* Push the ea_data followed by the name. */
		p += fill_ea_buffer(ctx, p, space_remaining, conn, name_list);
		nameptr = p;
		len = srvstr_push(base_data, flags2,
				  p + 1, fname, PTR_DIFF(end_data, p+1),
				  STR_TERMINATE | STR_NOALIGN);
		if (flags2 & FLAGS2_UNICODE_STRINGS) {
			if (len > 2) {
				len -= 2;
			} else {
				len = 0;
			}
		} else {
			if (len > 1) {
				len -= 1;
			} else {
				len = 0;
			}
		}
		SCVAL(nameptr,0,len);
		p += len + 1;
		SCVAL(p,0,0); p += 1; /* Extra zero byte ? - why.. */
		break;
	}

	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_BOTH_DIRECTORY_INFO\n"));
		was_8_3 = mangle_is_8_3(fname, True, conn->params);
		p += 4;
		SIVAL(p,0,reskey); p += 4;
		put_long_date_timespec(conn->ts_res,p,create_date_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,adate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,mdate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,cdate_ts); p += 8;
		SOFF_T(p,0,file_size); p += 8;
		SOFF_T(p,0,allocation_size); p += 8;
		SIVAL(p,0,mode); p += 4;
		q = p; p += 4; /* q is placeholder for name length. */
		{
			unsigned int ea_size = estimate_ea_size(conn, NULL,
								smb_fname->base_name);
			SIVAL(p,0,ea_size); /* Extended attributes */
			p += 4;
		}
		/* Clear the short name buffer. This is
		 * IMPORTANT as not doing so will trigger
		 * a Win2k client bug. JRA.
		 */
		if (!was_8_3 && check_mangled_names) {
			char mangled_name[13]; /* mangled 8.3 name. */
			if (!name_to_8_3(fname,mangled_name,True,
					   conn->params)) {
				/* Error - mangle failed ! */
				memset(mangled_name,'\0',12);
			}
			mangled_name[12] = 0;
			len = srvstr_push(base_data, flags2,
					  p+2, mangled_name, 24,
					  STR_UPPER|STR_UNICODE);
			if (len < 24) {
				memset(p + 2 + len,'\0',24 - len);
			}
			SSVAL(p, 0, len);
		} else {
			memset(p,'\0',26);
		}
		p += 2 + 24;
		len = srvstr_push(base_data, flags2, p,
				  fname, PTR_DIFF(end_data, p),
				  STR_TERMINATE_ASCII);
		SIVAL(q,0,len);
		p += len;

		len = PTR_DIFF(p, pdata);
		pad = (len + (align-1)) & ~(align-1);
		/*
		 * offset to the next entry, the caller
		 * will overwrite it for the last entry
		 * that's why we always include the padding
		 */
		SIVAL(pdata,0,pad);
		/*
		 * set padding to zero
		 */
		if (do_pad) {
			memset(p, 0, pad - len);
			p = pdata + pad;
		} else {
			p = pdata + len;
		}
		break;

	case SMB_FIND_FILE_DIRECTORY_INFO:
		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_DIRECTORY_INFO\n"));
		p += 4;
		SIVAL(p,0,reskey); p += 4;
		put_long_date_timespec(conn->ts_res,p,create_date_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,adate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,mdate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,cdate_ts); p += 8;
		SOFF_T(p,0,file_size); p += 8;
		SOFF_T(p,0,allocation_size); p += 8;
		SIVAL(p,0,mode); p += 4;
		len = srvstr_push(base_data, flags2,
				  p + 4, fname, PTR_DIFF(end_data, p+4),
				  STR_TERMINATE_ASCII);
		SIVAL(p,0,len);
		p += 4 + len;

		len = PTR_DIFF(p, pdata);
		pad = (len + (align-1)) & ~(align-1);
		/*
		 * offset to the next entry, the caller
		 * will overwrite it for the last entry
		 * that's why we always include the padding
		 */
		SIVAL(pdata,0,pad);
		/*
		 * set padding to zero
		 */
		if (do_pad) {
			memset(p, 0, pad - len);
			p = pdata + pad;
		} else {
			p = pdata + len;
		}
		break;

	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_FULL_DIRECTORY_INFO\n"));
		p += 4;
		SIVAL(p,0,reskey); p += 4;
		put_long_date_timespec(conn->ts_res,p,create_date_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,adate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,mdate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,cdate_ts); p += 8;
		SOFF_T(p,0,file_size); p += 8;
		SOFF_T(p,0,allocation_size); p += 8;
		SIVAL(p,0,mode); p += 4;
		q = p; p += 4; /* q is placeholder for name length. */
		{
			unsigned int ea_size = estimate_ea_size(conn, NULL,
								smb_fname->base_name);
			SIVAL(p,0,ea_size); /* Extended attributes */
			p +=4;
		}
		len = srvstr_push(base_data, flags2, p,
				  fname, PTR_DIFF(end_data, p),
				  STR_TERMINATE_ASCII);
		SIVAL(q, 0, len);
		p += len;

		len = PTR_DIFF(p, pdata);
		pad = (len + (align-1)) & ~(align-1);
		/*
		 * offset to the next entry, the caller
		 * will overwrite it for the last entry
		 * that's why we always include the padding
		 */
		SIVAL(pdata,0,pad);
		/*
		 * set padding to zero
		 */
		if (do_pad) {
			memset(p, 0, pad - len);
			p = pdata + pad;
		} else {
			p = pdata + len;
		}
		break;

	case SMB_FIND_FILE_NAMES_INFO:
		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_NAMES_INFO\n"));
		p += 4;
		SIVAL(p,0,reskey); p += 4;
		p += 4;
		/* this must *not* be null terminated or w2k gets in a loop trying to set an
		   acl on a dir (tridge) */
		len = srvstr_push(base_data, flags2, p,
				  fname, PTR_DIFF(end_data, p),
				  STR_TERMINATE_ASCII);
		SIVAL(p, -4, len);
		p += len;

		len = PTR_DIFF(p, pdata);
		pad = (len + (align-1)) & ~(align-1);
		/*
		 * offset to the next entry, the caller
		 * will overwrite it for the last entry
		 * that's why we always include the padding
		 */
		SIVAL(pdata,0,pad);
		/*
		 * set padding to zero
		 */
		if (do_pad) {
			memset(p, 0, pad - len);
			p = pdata + pad;
		} else {
			p = pdata + len;
		}
		break;

	case SMB_FIND_ID_FULL_DIRECTORY_INFO:
		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_ID_FULL_DIRECTORY_INFO\n"));
		p += 4;
		SIVAL(p,0,reskey); p += 4;
		put_long_date_timespec(conn->ts_res,p,create_date_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,adate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,mdate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,cdate_ts); p += 8;
		SOFF_T(p,0,file_size); p += 8;
		SOFF_T(p,0,allocation_size); p += 8;
		SIVAL(p,0,mode); p += 4;
		q = p; p += 4; /* q is placeholder for name length. */
		{
			unsigned int ea_size = estimate_ea_size(conn, NULL,
								smb_fname->base_name);
			SIVAL(p,0,ea_size); /* Extended attributes */
			p +=4;
		}
		SIVAL(p,0,0); p += 4; /* Unknown - reserved ? */
		SIVAL(p,0,smb_fname->st.st_ex_ino); p += 4; /* FileIndexLow */
		SIVAL(p,0,smb_fname->st.st_ex_dev); p += 4; /* FileIndexHigh */
		len = srvstr_push(base_data, flags2, p,
				  fname, PTR_DIFF(end_data, p),
				  STR_TERMINATE_ASCII);
		SIVAL(q, 0, len);
		p += len;

		len = PTR_DIFF(p, pdata);
		pad = (len + (align-1)) & ~(align-1);
		/*
		 * offset to the next entry, the caller
		 * will overwrite it for the last entry
		 * that's why we always include the padding
		 */
		SIVAL(pdata,0,pad);
		/*
		 * set padding to zero
		 */
		if (do_pad) {
			memset(p, 0, pad - len);
			p = pdata + pad;
		} else {
			p = pdata + len;
		}
		break;

	case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
		DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_ID_BOTH_DIRECTORY_INFO\n"));
		was_8_3 = mangle_is_8_3(fname, True, conn->params);
		p += 4;
		SIVAL(p,0,reskey); p += 4;
		put_long_date_timespec(conn->ts_res,p,create_date_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,adate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,mdate_ts); p += 8;
		put_long_date_timespec(conn->ts_res,p,cdate_ts); p += 8;
		SOFF_T(p,0,file_size); p += 8;
		SOFF_T(p,0,allocation_size); p += 8;
		SIVAL(p,0,mode); p += 4;
		q = p; p += 4; /* q is placeholder for name length */
		{
			unsigned int ea_size = estimate_ea_size(conn, NULL,
								smb_fname->base_name);
			SIVAL(p,0,ea_size); /* Extended attributes */
			p +=4;
		}
		/* Clear the short name buffer. This is
		 * IMPORTANT as not doing so will trigger
		 * a Win2k client bug. JRA.
		 */
		if (!was_8_3 && check_mangled_names) {
			char mangled_name[13]; /* mangled 8.3 name. */
			if (!name_to_8_3(fname,mangled_name,True,
					conn->params)) {
				/* Error - mangle failed ! */
				memset(mangled_name,'\0',12);
			}
			mangled_name[12] = 0;
			len = srvstr_push(base_data, flags2,
					  p+2, mangled_name, 24,
					  STR_UPPER|STR_UNICODE);
			SSVAL(p, 0, len);
			if (len < 24) {
				memset(p + 2 + len,'\0',24 - len);
			}
			SSVAL(p, 0, len);
		} else {
			memset(p,'\0',26);
		}
		p += 26;
		SSVAL(p,0,0); p += 2; /* Reserved ? */
		SIVAL(p,0,smb_fname->st.st_ex_ino); p += 4; /* FileIndexLow */
		SIVAL(p,0,smb_fname->st.st_ex_dev); p += 4; /* FileIndexHigh */
		len = srvstr_push(base_data, flags2, p,
				  fname, PTR_DIFF(end_data, p),
				  STR_TERMINATE_ASCII);
		SIVAL(q,0,len);
		p += len;

		len = PTR_DIFF(p, pdata);
		pad = (len + (align-1)) & ~(align-1);
		/*
		 * offset to the next entry, the caller
		 * will overwrite it for the last entry
		 * that's why we always include the padding
		 */
		SIVAL(pdata,0,pad);
		/*
		 * set padding to zero
		 */
		if (do_pad) {
			memset(p, 0, pad - len);
			p = pdata + pad;
		} else {
			p = pdata + len;
		}
		break;

	/* CIFS UNIX Extension. */

	case SMB_FIND_FILE_UNIX:
	case SMB_FIND_FILE_UNIX_INFO2:
		p+= 4;
		SIVAL(p,0,reskey); p+= 4;    /* Used for continuing search. */

		/* Begin of SMB_QUERY_FILE_UNIX_BASIC */

		if (info_level == SMB_FIND_FILE_UNIX) {
			DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_UNIX\n"));
			p = store_file_unix_basic(conn, p,
						NULL, &smb_fname->st);
			len = srvstr_push(base_data, flags2, p,
					  fname, PTR_DIFF(end_data, p),
					  STR_TERMINATE);
		} else {
			DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_UNIX_INFO2\n"));
			p = store_file_unix_basic_info2(conn, p,
						NULL, &smb_fname->st);
			nameptr = p;
			p += 4;
			len = srvstr_push(base_data, flags2, p, fname,
					  PTR_DIFF(end_data, p), 0);
			SIVAL(nameptr, 0, len);
		}

		p += len;

		len = PTR_DIFF(p, pdata);
		pad = (len + (align-1)) & ~(align-1);
		/*
		 * offset to the next entry, the caller
		 * will overwrite it for the last entry
		 * that's why we always include the padding
		 */
		SIVAL(pdata,0,pad);
		/*
		 * set padding to zero
		 */
		if (do_pad) {
			memset(p, 0, pad - len);
			p = pdata + pad;
		} else {
			p = pdata + len;
		}
		/* End of SMB_QUERY_FILE_UNIX_BASIC */

		break;

	default:
		return false;
	}

	if (PTR_DIFF(p,pdata) > space_remaining) {
		*out_of_space = true;
		DEBUG(9,("smbd_marshall_dir_entry: out of space\n"));
		return false; /* Not finished - just out of space */
	}

	/* Setup the last entry pointer, as an offset from base_data */
	*last_entry_off = PTR_DIFF(last_entry_ptr,base_data);
	/* Advance the data pointer to the next slot */
	*ppdata = p;

	return true;
}


int smb_vfs_call_fsetxattr(struct vfs_handle_struct *handle,
			   struct files_struct *fsp, const char *name,
			   const void *value, size_t size, int flags)
{
	VFS_FIND(fsetxattr);
	return handle->fns->fsetxattr(handle, fsp, name, value, size, flags);
}


static struct ea_list *read_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size)
{
	struct ea_list *ea_list_head = NULL;
	size_t offset = 0;
	size_t bytes_used = 0;

	while (offset < data_size) {
		struct ea_list *eal = read_ea_list_entry(ctx, pdata + offset, data_size - offset, &bytes_used);

		if (!eal) {
			return NULL;
		}

		DLIST_ADD_END(ea_list_head, eal, struct ea_list *);
		offset += bytes_used;
	}

	return ea_list_head;
}


static NTSTATUS fill_ea_chained_buffer(TALLOC_CTX *mem_ctx,
				       char *pdata,
				       unsigned int total_data_size,
				       unsigned int *ret_data_size,
				       connection_struct *conn,
				       struct ea_list *ea_list)
{
	uint8_t *p = (uint8_t *)pdata;
	uint8_t *last_start = NULL;

	*ret_data_size = 0;

	if (!lp_ea_support(SNUM(conn))) {
		return NT_STATUS_NO_EAS_ON_FILE;
	}

	for (; ea_list; ea_list = ea_list->next) {
		size_t dos_namelen;
		fstring dos_ea_name;
		size_t this_size;

		if (last_start) {
			SIVAL(last_start, 0, PTR_DIFF(p, last_start));
		}
		last_start = p;

		push_ascii_fstring(dos_ea_name, ea_list->ea.name);
		dos_namelen = strlen(dos_ea_name);
		if (dos_namelen > 255 || dos_namelen == 0) {
			return NT_STATUS_INTERNAL_ERROR;
		}
		if (ea_list->ea.value.length > 65535) {
			return NT_STATUS_INTERNAL_ERROR;
		}

		this_size = 0x08 + dos_namelen + 1 + ea_list->ea.value.length;

		if (ea_list->next) {
			size_t pad = 4 - (this_size % 4);
			this_size += pad;
		}

		if (this_size > total_data_size) {
			return NT_STATUS_INFO_LENGTH_MISMATCH;
		}

		/* We know we have room. */
		SIVAL(p, 0x00, 0); /* next offset */
		SCVAL(p, 0x04, ea_list->ea.flags);
		SCVAL(p, 0x05, dos_namelen);
		SSVAL(p, 0x06, ea_list->ea.value.length);
		fstrcpy((char *)(p+0x08), dos_ea_name);
		memcpy(p + 0x08 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);

		total_data_size -= this_size;
		p += this_size;
	}

	*ret_data_size = PTR_DIFF(p, pdata);
	DEBUG(10,("fill_ea_chained_buffer: data_size = %u\n", *ret_data_size));
	return NT_STATUS_OK;
}


ssize_t smb_vfs_call_recvfile(struct vfs_handle_struct *handle, int fromfd,
			      files_struct *tofsp, SMB_OFF_T offset,
			      size_t count)
{
	VFS_FIND(recvfile);
	return handle->fns->recvfile(handle, fromfd, tofsp, offset, count);
}


static bool samba_private_attr_name(const char *unix_ea_name)
{
	static const char * const prohibited_ea_names[] = {
		SAMBA_POSIX_INHERITANCE_EA_NAME,
		SAMBA_XATTR_DOS_ATTRIB,
		SAMBA_XATTR_MARKER,
		XATTR_NTACL_NAME,
		NULL
	};

	int i;

	for (i = 0; prohibited_ea_names[i]; i++) {
		if (strequal( prohibited_ea_names[i], unix_ea_name))
			return true;
	}
	if (StrnCaseCmp(unix_ea_name, SAMBA_XATTR_DOSSTREAM_PREFIX,
			strlen(SAMBA_XATTR_DOSSTREAM_PREFIX)) == 0) {
		return true;
	}
	return false;
}


struct file_id vfs_file_id_from_sbuf(connection_struct *conn, const SMB_STRUCT_STAT *sbuf)
{
	return SMB_VFS_FILE_ID_CREATE(conn, sbuf);
}


int smb_vfs_call_removexattr(struct vfs_handle_struct *handle,
			     const char *path, const char *name)
{
	VFS_FIND(removexattr);
	return handle->fns->removexattr(handle, path, name);
}


static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
					struct smb_request *req,
					const char *pdata,
					int total_data,
					files_struct *fsp,
					const struct smb_filename *smb_fname)
{
	struct smb_file_time ft;
	uint32 raw_unixmode;
	mode_t unixmode;
	SMB_OFF_T size = 0;
	uid_t set_owner = (uid_t)SMB_UID_NO_CHANGE;
	gid_t set_grp = (uid_t)SMB_GID_NO_CHANGE;
	NTSTATUS status = NT_STATUS_OK;
	bool delete_on_fail = False;
	enum perm_type ptype;
	files_struct *all_fsps = NULL;
	bool modify_mtime = true;
	struct file_id id;
	SMB_STRUCT_STAT sbuf;

	ZERO_STRUCT(ft);

	if (total_data < 100) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if(IVAL(pdata, 0) != SMB_SIZE_NO_CHANGE_LO &&
	   IVAL(pdata, 4) != SMB_SIZE_NO_CHANGE_HI) {
		size=IVAL(pdata,0); /* first 8 Bytes are size */
#ifdef LARGE_SMB_OFF_T
		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 */
	}

	ft.atime = interpret_long_date(pdata+24); /* access_time */
	ft.mtime = interpret_long_date(pdata+32); /* modification_time */
	set_owner = (uid_t)IVAL(pdata,40);
	set_grp = (gid_t)IVAL(pdata,48);
	raw_unixmode = IVAL(pdata,84);

	if (VALID_STAT(smb_fname->st)) {
		if (S_ISDIR(smb_fname->st.st_ex_mode)) {
			ptype = PERM_EXISTING_DIR;
		} else {
			ptype = PERM_EXISTING_FILE;
		}
	} else {
		ptype = PERM_NEW_FILE;
	}

	status = unix_perms_from_wire(conn, &smb_fname->st, raw_unixmode,
				      ptype, &unixmode);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	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",
		  smb_fname_str_dbg(smb_fname), (double)size,
		  (unsigned int)set_owner, (unsigned int)set_grp,
		  (int)raw_unixmode));

	sbuf = smb_fname->st;

	if (!VALID_STAT(sbuf)) {
		struct smb_filename *smb_fname_tmp = NULL;
		/*
		 * 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.
		 */

		status = smb_unix_mknod(conn,
					pdata,
					total_data,
					smb_fname);
		if (!NT_STATUS_IS_OK(status)) {
			return status;
		}

		status = copy_smb_filename(talloc_tos(), smb_fname,
					   &smb_fname_tmp);
		if (!NT_STATUS_IS_OK(status)) {
			return status;
		}

		if (SMB_VFS_STAT(conn, smb_fname_tmp) != 0) {
			status = map_nt_error_from_unix(errno);
			TALLOC_FREE(smb_fname_tmp);
			SMB_VFS_UNLINK(conn, smb_fname);
			return status;
		}

		sbuf = smb_fname_tmp->st;
		TALLOC_FREE(smb_fname_tmp);

		/* Ensure we don't try and change anything else. */
		raw_unixmode = SMB_MODE_NO_CHANGE;
		size = get_file_size_stat(&sbuf);
		ft.atime = sbuf.st_ex_atime;
		ft.mtime = sbuf.st_ex_mtime;
		/*
		 * We continue here as we might want to change the
		 * owner uid/gid.
		 */
		delete_on_fail = True;
	}

#if 1
	/* Horrible backwards compatibility hack as an old server bug
	 * allowed a CIFS client bug to remain unnoticed :-(. JRA.
	 * */

	if (!size) {
		size = get_file_size_stat(&sbuf);
	}
#endif

	/*
	 * 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)unixmode,
			  smb_fname_str_dbg(smb_fname)));
		if (SMB_VFS_CHMOD(conn, smb_fname->base_name, 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) &&
	    (sbuf.st_ex_uid != set_owner)) {
		int ret;

		DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC "
			  "changing owner %u for path %s\n",
			  (unsigned int)set_owner,
			  smb_fname_str_dbg(smb_fname)));

		if (S_ISLNK(sbuf.st_ex_mode)) {
			ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name,
					     set_owner, (gid_t)-1);
		} else {
			ret = SMB_VFS_CHOWN(conn, smb_fname->base_name,
					    set_owner, (gid_t)-1);
		}

		if (ret != 0) {
			status = map_nt_error_from_unix(errno);
			if (delete_on_fail) {
				SMB_VFS_UNLINK(conn, smb_fname);
			}
			return status;
		}
	}

	/*
	 * Deal with the UNIX specific gid set.
	 */

	if ((set_grp != (uid_t)SMB_GID_NO_CHANGE) &&
	    (sbuf.st_ex_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,
			  smb_fname_str_dbg(smb_fname)));
		if (SMB_VFS_CHOWN(conn, smb_fname->base_name, (uid_t)-1,
				  set_grp) != 0) {
			status = map_nt_error_from_unix(errno);
			if (delete_on_fail) {
				SMB_VFS_UNLINK(conn, smb_fname);
			}
			return status;
		}
	}

	/* Deal with any size changes. */

	status = smb_set_file_size(conn, req,
				   fsp,
				   smb_fname,
				   &sbuf,
				   size,
				   false);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	/* Deal with any time changes. */
	if (null_timespec(ft.mtime) && null_timespec(ft.atime)) {
		/* No change, don't cancel anything. */
		return status;
	}

	id = vfs_file_id_from_sbuf(conn, &sbuf);
	for(all_fsps = file_find_di_first(id); all_fsps;
			all_fsps = file_find_di_next(all_fsps)) {
		/*
		 * We're setting the time explicitly for UNIX.
		 * Cancel any pending changes over all handles.
		 */
		all_fsps->update_write_time_on_close = false;
		TALLOC_FREE(all_fsps->update_write_time_event);
	}

	/*
	 * Override the "setting_write_time"
	 * parameter here as it almost does what
	 * we need. Just remember if we modified
	 * mtime and send the notify ourselves.
	 */
	if (null_timespec(ft.mtime)) {
		modify_mtime = false;
	}

	status = smb_set_file_time(conn,
				fsp,
				smb_fname,
				&ft,
				false);
	if (modify_mtime) {
		notify_fname(conn, NOTIFY_ACTION_MODIFIED,
			FILE_NOTIFY_CHANGE_LAST_WRITE, smb_fname->base_name);
	}
	return status;
}


int smb_vfs_call_aio_error_fn(struct vfs_handle_struct *handle,
			      struct files_struct *fsp,
			      SMB_STRUCT_AIOCB *aiocb)
{
	VFS_FIND(aio_error_fn);
	return handle->fns->aio_error_fn(handle, fsp, aiocb);
}


static void call_trans2setfsinfo(connection_struct *conn,
				 struct smb_request *req,
				 char **pparams, int total_params,
				 char **ppdata, int total_data,
				 unsigned int max_data_bytes)
{
	char *pdata = *ppdata;
	char *params = *pparams;
	uint16 info_level;

	DEBUG(10,("call_trans2setfsinfo: for service [%s]\n",lp_servicename(SNUM(conn))));

	/*  */
	if (total_params < 4) {
		DEBUG(0,("call_trans2setfsinfo: requires total_params(%d) >= 4 bytes!\n",
			total_params));
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return;
	}

	info_level = SVAL(params,2);

	if (IS_IPC(conn)) {
		if (info_level != SMB_REQUEST_TRANSPORT_ENCRYPTION &&
				info_level != SMB_SET_CIFS_UNIX_INFO) {
			DEBUG(0,("call_trans2setfsinfo: not an allowed "
				"info level (0x%x) on IPC$.\n",
				(unsigned int)info_level));
			reply_nterror(req, NT_STATUS_ACCESS_DENIED);
			return;
		}
	}

	if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) {
		if (info_level != SMB_REQUEST_TRANSPORT_ENCRYPTION) {
			DEBUG(0,("call_trans2setfsinfo: encryption required "
				"and info level 0x%x sent.\n",
				(unsigned int)info_level));
			exit_server_cleanly("encryption required "
				"on connection");
			return;
		}
	}

	switch(info_level) {
		case SMB_SET_CIFS_UNIX_INFO:
			{
				uint16 client_unix_major;
				uint16 client_unix_minor;
				uint32 client_unix_cap_low;
				uint32 client_unix_cap_high;

				if (!lp_unix_extensions()) {
					reply_nterror(req,
						      NT_STATUS_INVALID_LEVEL);
					return;
				}

				/* There should be 12 bytes of capabilities set. */
				if (total_data < 8) {
					reply_nterror(
						req,
						NT_STATUS_INVALID_PARAMETER);
					return;
				}
				client_unix_major = SVAL(pdata,0);
				client_unix_minor = SVAL(pdata,2);
				client_unix_cap_low = IVAL(pdata,4);
				client_unix_cap_high = IVAL(pdata,8);
				/* Just print these values for now. */
				DEBUG(10,("call_trans2setfsinfo: set unix info. major = %u, minor = %u \
cap_low = 0x%x, cap_high = 0x%x\n",
					(unsigned int)client_unix_major,
					(unsigned int)client_unix_minor,
					(unsigned int)client_unix_cap_low,
					(unsigned int)client_unix_cap_high ));

				/* Here is where we must switch to posix pathname processing... */
				if (client_unix_cap_low & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
					lp_set_posix_pathnames();
					mangle_change_to_posix();
				}

				if ((client_unix_cap_low & CIFS_UNIX_FCNTL_LOCKS_CAP) &&
				    !(client_unix_cap_low & CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP)) {
					/* Client that knows how to do posix locks,
					 * but not posix open/mkdir operations. Set a
					 * default type for read/write checks. */

					lp_set_posix_default_cifsx_readwrite_locktype(POSIX_LOCK);

				}
				break;
			}

		case SMB_REQUEST_TRANSPORT_ENCRYPTION:
			{
				NTSTATUS status;
				size_t param_len = 0;
				size_t data_len = total_data;

				if (!lp_unix_extensions()) {
					reply_nterror(
						req,
						NT_STATUS_INVALID_LEVEL);
					return;
				}

				if (lp_smb_encrypt(SNUM(conn)) == false) {
					reply_nterror(
						req,
						NT_STATUS_NOT_SUPPORTED);
					return;
				}

				DEBUG( 4,("call_trans2setfsinfo: "
					"request transport encryption.\n"));

				status = srv_request_encryption_setup(conn,
								(unsigned char **)ppdata,
								&data_len,
								(unsigned char **)pparams,
								&param_len);

				if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
						!NT_STATUS_IS_OK(status)) {
					reply_nterror(req, status);
					return;
				}

				send_trans2_replies(conn, req,
						*pparams,
						param_len,
						*ppdata,
						data_len,
						max_data_bytes);

				if (NT_STATUS_IS_OK(status)) {
					/* Server-side transport
					 * encryption is now *on*. */
					status = srv_encryption_start(conn);
					if (!NT_STATUS_IS_OK(status)) {
						exit_server_cleanly(
							"Failure in setting "
							"up encrypted transport");
					}
				}
				return;
			}

		case SMB_FS_QUOTA_INFORMATION:
			{
				files_struct *fsp = NULL;
				SMB_NTQUOTA_STRUCT quotas;

				ZERO_STRUCT(quotas);

				/* access check */
				if ((conn->server_info->utok.uid != sec_initial_uid())
				    ||!CAN_WRITE(conn)) {
					DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
						 lp_servicename(SNUM(conn)),
						 conn->server_info->unix_name));
					reply_nterror(req, NT_STATUS_ACCESS_DENIED);
					return;
				}

				/* note: normaly there're 48 bytes,
				 * but we didn't use the last 6 bytes for now
				 * --metze
				 */
				fsp = file_fsp(req, SVAL(params,0));

				if (!check_fsp_ntquota_handle(conn, req,
							      fsp)) {
					DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
					reply_nterror(
						req, NT_STATUS_INVALID_HANDLE);
					return;
				}

				if (total_data < 42) {
					DEBUG(0,("call_trans2setfsinfo: SET_FS_QUOTA: requires total_data(%d) >= 42 bytes!\n",
						total_data));
					reply_nterror(
						req,
						NT_STATUS_INVALID_PARAMETER);
					return;
				}

				/* unknown_1 24 NULL bytes in pdata*/

				/* the soft quotas 8 bytes (uint64_t)*/
				quotas.softlim = (uint64_t)IVAL(pdata,24);
#ifdef LARGE_SMB_OFF_T
				quotas.softlim |= (((uint64_t)IVAL(pdata,28)) << 32);
#else /* LARGE_SMB_OFF_T */
				if ((IVAL(pdata,28) != 0)&&
					((quotas.softlim != 0xFFFFFFFF)||
					(IVAL(pdata,28)!=0xFFFFFFFF))) {
					/* more than 32 bits? */
					reply_nterror(
						req,
						NT_STATUS_INVALID_PARAMETER);
					return;
				}
#endif /* LARGE_SMB_OFF_T */

				/* the hard quotas 8 bytes (uint64_t)*/
				quotas.hardlim = (uint64_t)IVAL(pdata,32);
#ifdef LARGE_SMB_OFF_T
				quotas.hardlim |= (((uint64_t)IVAL(pdata,36)) << 32);
#else /* LARGE_SMB_OFF_T */
				if ((IVAL(pdata,36) != 0)&&
					((quotas.hardlim != 0xFFFFFFFF)||
					(IVAL(pdata,36)!=0xFFFFFFFF))) {
					/* more than 32 bits? */
					reply_nterror(
						req,
						NT_STATUS_INVALID_PARAMETER);
					return;
				}
#endif /* LARGE_SMB_OFF_T */

				/* quota_flags 2 bytes **/
				quotas.qflags = SVAL(pdata,40);

				/* unknown_2 6 NULL bytes follow*/

				/* now set the quotas */
				if (vfs_set_ntquota(fsp, SMB_USER_FS_QUOTA_TYPE, NULL, &quotas)!=0) {
					DEBUG(0,("vfs_set_ntquota() failed for service [%s]\n",lp_servicename(SNUM(conn))));
					reply_nterror(req, map_nt_error_from_unix(errno));
					return;
				}

				break;
			}
		default:
			DEBUG(3,("call_trans2setfsinfo: unknown level (0x%X) not implemented yet.\n",
				info_level));
			reply_nterror(req, NT_STATUS_INVALID_LEVEL);
			return;
			break;
	}

	/*
	 * sending this reply works fine,
	 * but I'm not sure it's the same
	 * like windows do...
	 * --metze
	 */
	reply_outbuf(req, 10, 0);
}


static char *store_file_unix_basic_info2(connection_struct *conn,
				char *pdata,
				files_struct *fsp,
				const SMB_STRUCT_STAT *psbuf)
{
	uint32 file_flags = 0;
	uint32 flags_mask = 0;

	pdata = store_file_unix_basic(conn, pdata, fsp, psbuf);

	/* Create (birth) time 64 bit */
	put_long_date_timespec(TIMESTAMP_SET_NT_OR_BETTER,pdata, psbuf->st_ex_btime);
	pdata += 8;

	map_info2_flags_from_sbuf(psbuf, &file_flags, &flags_mask);
	SIVAL(pdata, 0, file_flags); /* flags */
	SIVAL(pdata, 4, flags_mask); /* mask */
	pdata += 8;

	return pdata;
}


void reply_findclose(struct smb_request *req)
{
	int dptr_num;
	struct smbd_server_connection *sconn = smbd_server_conn;

	START_PROFILE(SMBfindclose);

	if (req->wct < 1) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		END_PROFILE(SMBfindclose);
		return;
	}

	dptr_num = SVALS(req->vwv+0, 0);

	DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num));

	dptr_close(sconn, &dptr_num);

	reply_outbuf(req, 0, 0);

	DEBUG(3,("SMBfindclose dptr_num = %d\n", dptr_num));

	END_PROFILE(SMBfindclose);
	return;
}


static bool marshall_posix_acl(connection_struct *conn, char *pdata, SMB_STRUCT_STAT *pst, SMB_ACL_T posix_acl)
{
	int entry_id = SMB_ACL_FIRST_ENTRY;
	SMB_ACL_ENTRY_T entry;

	while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
		SMB_ACL_TAG_T tagtype;
		SMB_ACL_PERMSET_T permset;
		unsigned char perms = 0;
		unsigned int own_grp;

		/* get_next... */
		if (entry_id == SMB_ACL_FIRST_ENTRY) {
			entry_id = SMB_ACL_NEXT_ENTRY;
		}

		if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
			DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_TAG_TYPE failed.\n"));
			return False;
		}

		if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
			DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_PERMSET failed.\n"));
			return False;
		}

		perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? SMB_POSIX_ACL_READ : 0);
		perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? SMB_POSIX_ACL_WRITE : 0);
		perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? SMB_POSIX_ACL_EXECUTE : 0);

		SCVAL(pdata,1,perms);

		switch (tagtype) {
			case SMB_ACL_USER_OBJ:
				SCVAL(pdata,0,SMB_POSIX_ACL_USER_OBJ);
				own_grp = (unsigned int)pst->st_ex_uid;
				SIVAL(pdata,2,own_grp);
				SIVAL(pdata,6,0);
				break;
			case SMB_ACL_USER:
				{
					uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
					if (!puid) {
						DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n"));
						return False;
					}
					own_grp = (unsigned int)*puid;
					SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
					SCVAL(pdata,0,SMB_POSIX_ACL_USER);
					SIVAL(pdata,2,own_grp);
					SIVAL(pdata,6,0);
					break;
				}
			case SMB_ACL_GROUP_OBJ:
				SCVAL(pdata,0,SMB_POSIX_ACL_GROUP_OBJ);
				own_grp = (unsigned int)pst->st_ex_gid;
				SIVAL(pdata,2,own_grp);
				SIVAL(pdata,6,0);
				break;
			case SMB_ACL_GROUP:
				{
					gid_t *pgid= (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
					if (!pgid) {
						DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n"));
						return False;
					}
					own_grp = (unsigned int)*pgid;
					SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
					SCVAL(pdata,0,SMB_POSIX_ACL_GROUP);
					SIVAL(pdata,2,own_grp);
					SIVAL(pdata,6,0);
					break;
				}
			case SMB_ACL_MASK:
				SCVAL(pdata,0,SMB_POSIX_ACL_MASK);
				SIVAL(pdata,2,0xFFFFFFFF);
				SIVAL(pdata,6,0xFFFFFFFF);
				break;
			case SMB_ACL_OTHER:
				SCVAL(pdata,0,SMB_POSIX_ACL_OTHER);
				SIVAL(pdata,2,0xFFFFFFFF);
				SIVAL(pdata,6,0xFFFFFFFF);
				break;
			default:
				DEBUG(0,("marshall_posix_acl: unknown tagtype.\n"));
				return False;
		}
		pdata += SMB_POSIX_ACL_ENTRY_SIZE;
	}

	return True;
}


NTSTATUS smb_vfs_call_get_nt_acl(struct vfs_handle_struct *handle,
				 const char *name,
				 uint32 security_info,
				 struct security_descriptor **ppdesc)
{
	VFS_FIND(get_nt_acl);
	return handle->fns->get_nt_acl(handle, name, security_info, ppdesc);
}


gs_window_destroy (GSWindow *window)
{
        g_return_if_fail (GS_IS_WINDOW (window));

        gs_window_cancel_unlock_request (window);

        gtk_widget_destroy (GTK_WIDGET (window));
}


get_cursor (void)
{
        GdkBitmap *empty_bitmap;
        GdkCursor *cursor;
        GdkColor   useless;
        char       invisible_cursor_bits [] = { 0x0 };

        useless.red = useless.green = useless.blue = 0;
        useless.pixel = 0;

        empty_bitmap = gdk_bitmap_create_from_data (NULL,
                                                    invisible_cursor_bits,
                                                    1, 1);

        cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
                                             empty_bitmap,
                                             &useless,
                                             &useless, 0, 0);

        g_object_unref (empty_bitmap);

        return cursor;
}


gs_grab_keyboard_reset (GSGrab *grab)
{
        if (grab->priv->keyboard_grab_window != NULL) {
                g_object_remove_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
                                              (gpointer *) &grab->priv->keyboard_grab_window);
        }
        grab->priv->keyboard_grab_window = NULL;
        grab->priv->keyboard_grab_screen = NULL;
}


gs_grab_get_keyboard (GSGrab    *grab,
                      GdkWindow *window,
                      GdkScreen *screen)
{
        GdkGrabStatus status;

        g_return_val_if_fail (window != NULL, FALSE);
        g_return_val_if_fail (screen != NULL, FALSE);

        gs_debug ("Grabbing keyboard widget=%X", (guint32) GDK_WINDOW_XID (window));
        status = gdk_keyboard_grab (window, FALSE, GDK_CURRENT_TIME);

        if (status == GDK_GRAB_SUCCESS) {
                if (grab->priv->keyboard_grab_window != NULL) {
                        g_object_remove_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
                                                      (gpointer *) &grab->priv->keyboard_grab_window);
                }
                grab->priv->keyboard_grab_window = window;

                g_object_add_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
                                           (gpointer *) &grab->priv->keyboard_grab_window);

                grab->priv->keyboard_grab_screen = screen;
        } else {
                gs_debug ("Couldn't grab keyboard!  (%s)", grab_string (status));
        }

        return status;
}


gs_listener_acquire (GSListener *listener,
                     GError    **error)
{
        gboolean  acquired;
        DBusError buserror;
        gboolean  is_connected;

        g_return_val_if_fail (listener != NULL, FALSE);

        if (! listener->priv->connection) {
                g_set_error (error,
                             GS_LISTENER_ERROR,
                             GS_LISTENER_ERROR_ACQUISITION_FAILURE,
                             "%s",
                             _("failed to register with the message bus"));
                return FALSE;
        }

        is_connected = dbus_connection_get_is_connected (listener->priv->connection);
        if (! is_connected) {
                g_set_error (error,
                             GS_LISTENER_ERROR,
                             GS_LISTENER_ERROR_ACQUISITION_FAILURE,
                             "%s",
                             _("not connected to the message bus"));
                return FALSE;
        }

        if (screensaver_is_running (listener->priv->connection)) {
                g_set_error (error,
                             GS_LISTENER_ERROR,
                             GS_LISTENER_ERROR_ACQUISITION_FAILURE,
                             "%s",
                             _("screensaver already running in this session"));
                return FALSE;
        }

        dbus_error_init (&buserror);

        if (dbus_connection_register_object_path (listener->priv->connection,
                                                  GS_LISTENER_PATH,
                                                  &gs_listener_vtable,
                                                  listener) == FALSE) {
                g_critical ("out of memory registering object path");
                return FALSE;
        }

        acquired = dbus_bus_request_name (listener->priv->connection,
                                          GS_LISTENER_SERVICE,
                                          0, &buserror) != -1;
        if (dbus_error_is_set (&buserror)) {
                g_set_error (error,
                             GS_LISTENER_ERROR,
                             GS_LISTENER_ERROR_ACQUISITION_FAILURE,
                             "%s",
                             buserror.message);
        }

        dbus_error_free (&buserror);

        dbus_connection_add_filter (listener->priv->connection, listener_dbus_filter_function, listener, NULL);

        dbus_bus_add_match (listener->priv->connection,
                            "type='signal'"
                            ",interface='"DBUS_INTERFACE_DBUS"'"
                            ",sender='"DBUS_SERVICE_DBUS"'"
                            ",member='NameOwnerChanged'",
                            NULL);

        if (listener->priv->system_connection != NULL) {
                dbus_connection_add_filter (listener->priv->system_connection,
                                            listener_dbus_system_filter_function,
                                            listener,
                                            NULL);

                dbus_bus_add_match (listener->priv->system_connection,
                                    "type='signal'"
                                    ",interface='"HAL_DEVICE_INTERFACE"'"
                                    ",member='Condition'",
                                    NULL);
                dbus_bus_add_match (listener->priv->system_connection,
                                    "type='signal'"
                                    ",interface='"CK_SESSION_INTERFACE"'"
                                    ",member='Unlock'",
                                    NULL);
                dbus_bus_add_match (listener->priv->system_connection,
                                    "type='signal'"
                                    ",interface='"CK_SESSION_INTERFACE"'"
                                    ",member='Lock'",
                                    NULL);
                dbus_bus_add_match (listener->priv->system_connection,
                                    "type='signal'"
                                    ",interface='"CK_SESSION_INTERFACE"'"
                                    ",member='ActiveChanged'",
                                    NULL);
        }

        return acquired;
}


raise_error (DBusConnection *connection,
             DBusMessage    *in_reply_to,
             const char     *error_name,
             char           *format, ...)
{
        char         buf[512];
        DBusMessage *reply;

        va_list args;
        va_start (args, format);
        vsnprintf (buf, sizeof (buf), format, args);
        va_end (args);

        gs_debug (buf);
        reply = dbus_message_new_error (in_reply_to, error_name, buf);
        if (reply == NULL) {
                g_error ("No memory");
        }
        if (! dbus_connection_send (connection, reply, NULL)) {
                g_error ("No memory");
        }

        dbus_message_unref (reply);
}


accumulate_ref_entry (gpointer            key,
                      GSListenerRefEntry *entry,
                      DBusMessageIter    *iter)
{
        char *description;
        char *time;

        time = _g_time_val_to_iso8601 (&entry->since);

        description = g_strdup_printf ("Application=\"%s\"; Since=\"%s\"; Reason=\"%s\";",
                                       entry->application,
                                       time,
                                       entry->reason);

        dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &description);

        g_free (description);
        g_free (time);
}


listener_dbus_add_ref_entry (GSListener     *listener,
                             int             entry_type,
                             DBusConnection *connection,
                             DBusMessage    *message)
{
        DBusMessage        *reply;
        DBusError           error;
        const char         *application;
        const char         *reason;
        GSListenerRefEntry *entry;
        DBusMessageIter     iter;

        dbus_error_init (&error);
        if (! dbus_message_get_args (message, &error,
                                     DBUS_TYPE_STRING, &application,
                                     DBUS_TYPE_STRING, &reason,
                                     DBUS_TYPE_INVALID)) {
                if (entry_type == REF_ENTRY_TYPE_INHIBIT) {
                        raise_syntax (connection, message, "Inhibit");
                } else if (entry_type == REF_ENTRY_TYPE_THROTTLE) {
                        raise_syntax (connection, message, "Throttle");
                } else {
                        g_assert_not_reached ();
                 }

                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL) {
                g_error ("No memory");
        }

        entry = g_new0 (GSListenerRefEntry, 1);
        entry->entry_type = entry_type;
        entry->connection = g_strdup (dbus_message_get_sender (message));
        entry->cookie = listener_generate_unique_key (listener, entry_type);
        entry->application = g_strdup (application);
        entry->reason = g_strdup (reason);
        g_get_current_time (&entry->since);

        listener_add_ref_entry (listener, entry_type, entry);

        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &entry->cookie);

        if (! dbus_connection_send (connection, reply, NULL)) {
                g_error ("No memory");
        }

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}


listener_get_active_time (GSListener     *listener,
                          DBusConnection *connection,
                          DBusMessage    *message)
{
        DBusMessageIter iter;
        DBusMessage    *reply;
        dbus_uint32_t    secs;

        reply = dbus_message_new_method_return (message);

        dbus_message_iter_init_append (reply, &iter);

        if (reply == NULL) {
                g_error ("No memory");
        }

        if (listener->priv->active) {
                time_t now = time (NULL);

                if (now < listener->priv->active_start) {
                        /* shouldn't happen */
                        gs_debug ("Active start time is in the future");
                        secs = 0;
                } else if (listener->priv->active_start <= 0) {
                        /* shouldn't happen */
                        gs_debug ("Active start time was not set");
                        secs = 0;
                } else {
                        secs = now - listener->priv->active_start;
                }
        } else {
                secs = 0;
        }

        gs_debug ("Returning screensaver active for %u seconds", secs);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &secs);

        if (! dbus_connection_send (connection, reply, NULL)) {
                g_error ("No memory");
        }

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}


gs_listener_set_activation_enabled (GSListener *listener,
                                    gboolean    enabled)
{
        g_return_if_fail (GS_IS_LISTENER (listener));

        if (listener->priv->activation_enabled != enabled) {
                listener->priv->activation_enabled = enabled;
        }
}


gs_listener_set_throttle (GSListener *listener,
                          gboolean    throttled)
{
        g_return_if_fail (GS_IS_LISTENER (listener));

        if (listener->priv->throttled != throttled) {
                gs_debug ("Changing throttle status: %d", throttled);

                listener->priv->throttled = throttled;

                g_signal_emit (listener, signals [THROTTLE_CHANGED], 0, throttled);
        }
}


gs_listener_message_handler (DBusConnection *connection,
                             DBusMessage    *message,
                             void           *user_data)
{
        g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
        g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);

#if 0
        g_message ("obj_path=%s interface=%s method=%s destination=%s",
                   dbus_message_get_path (message),
                   dbus_message_get_interface (message),
                   dbus_message_get_member (message),
                   dbus_message_get_destination (message));
#endif

        if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch")) {
                DBusMessage *reply;

                reply = dbus_message_new_method_return (message);

                if (reply == NULL) {
                        g_error ("No memory");
                }

                if (! dbus_connection_send (connection, reply, NULL)) {
                        g_error ("No memory");
                }

                dbus_message_unref (reply);

                return DBUS_HANDLER_RESULT_HANDLED;
        } else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
                   strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
                dbus_connection_unref (connection);

                return DBUS_HANDLER_RESULT_HANDLED;
        } else {
                return listener_dbus_handle_session_message (connection, message, user_data, TRUE);
        }
}


response_ok (void)
{
        printf ("RESPONSE=OK\n");
        fflush (stdout);
}


futex_wake_op(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
	      int nr_wake, int nr_wake2, int op)
{
	union futex_key key1 = FUTEX_KEY_INIT, key2 = FUTEX_KEY_INIT;
	struct futex_hash_bucket *hb1, *hb2;
	struct plist_head *head;
	struct futex_q *this, *next;
	int ret, op_ret;

retry:
	ret = get_futex_key(uaddr1, fshared, &key1);
	if (unlikely(ret != 0))
		goto out;
	ret = get_futex_key(uaddr2, fshared, &key2);
	if (unlikely(ret != 0))
		goto out_put_key1;

	hb1 = hash_futex(&key1);
	hb2 = hash_futex(&key2);

retry_private:
	double_lock_hb(hb1, hb2);
	op_ret = futex_atomic_op_inuser(op, uaddr2);
	if (unlikely(op_ret < 0)) {

		double_unlock_hb(hb1, hb2);

#ifndef CONFIG_MMU
		/*
		 * we don't get EFAULT from MMU faults if we don't have an MMU,
		 * but we might get them from range checking
		 */
		ret = op_ret;
		goto out_put_keys;
#endif

		if (unlikely(op_ret != -EFAULT)) {
			ret = op_ret;
			goto out_put_keys;
		}

		ret = fault_in_user_writeable(uaddr2);
		if (ret)
			goto out_put_keys;

		if (!fshared)
			goto retry_private;

		put_futex_key(fshared, &key2);
		put_futex_key(fshared, &key1);
		goto retry;
	}

	head = &hb1->chain;

	plist_for_each_entry_safe(this, next, head, list) {
		if (match_futex (&this->key, &key1)) {
			wake_futex(this);
			if (++ret >= nr_wake)
				break;
		}
	}

	if (op_ret > 0) {
		head = &hb2->chain;

		op_ret = 0;
		plist_for_each_entry_safe(this, next, head, list) {
			if (match_futex (&this->key, &key2)) {
				wake_futex(this);
				if (++op_ret >= nr_wake2)
					break;
			}
		}
		ret += op_ret;
	}

	double_unlock_hb(hb1, hb2);
out_put_keys:
	put_futex_key(fshared, &key2);
out_put_key1:
	put_futex_key(fshared, &key1);
out:
	return ret;
}


static struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb,
					union futex_key *key)
{
	struct futex_q *this;

	plist_for_each_entry(this, &hb->chain, list) {
		if (match_futex(&this->key, key))
			return this;
	}
	return NULL;
}


int gnutls_x509_crt_get_issuer_dn_by_oid(gnutls_x509_crt_t cert,
					 const char *oid, int indx,
					 unsigned int raw_flag, void *buf,
					 size_t * sizeof_buf)
{
    if (cert == NULL) {
	gnutls_assert();
	return GNUTLS_E_INVALID_REQUEST;
    }

    return _gnutls_x509_parse_dn_oid(cert->cert,
				     "tbsCertificate.issuer.rdnSequence",
				     oid, indx, raw_flag, buf, sizeof_buf);
}


int gnutls_x509_crt_get_subject_key_id(gnutls_x509_crt_t cert, void *ret,
				       size_t * ret_size,
				       unsigned int *critical)
{
    int result, len;
    gnutls_datum_t id;
    ASN1_TYPE c2 = ASN1_TYPE_EMPTY;

    if (cert == NULL) {
	gnutls_assert();
	return GNUTLS_E_INVALID_REQUEST;
    }

    if (ret)
	memset(ret, 0, *ret_size);
    else
	*ret_size = 0;

    if ((result =
	 _gnutls_x509_crt_get_extension(cert, "2.5.29.14", 0, &id,
					critical)) < 0) {
	return result;
    }

    if (id.size == 0 || id.data == NULL) {
	gnutls_assert();
	return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
    }

    result = asn1_create_element
	(_gnutls_get_pkix(), "PKIX1.SubjectKeyIdentifier", &c2);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	_gnutls_free_datum(&id);
	return _gnutls_asn2err(result);
    }

    result = asn1_der_decoding(&c2, id.data, id.size, NULL);
    _gnutls_free_datum(&id);

    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	asn1_delete_structure(&c2);
	return _gnutls_asn2err(result);
    }

    len = *ret_size;
    result = asn1_read_value(c2, "", ret, &len);

    *ret_size = len;
    asn1_delete_structure(&c2);

    if (result == ASN1_VALUE_NOT_FOUND || result == ASN1_ELEMENT_NOT_FOUND) {
	return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
    }

    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	return _gnutls_asn2err(result);
    }

    return 0;
}


int gnutls_x509_crt_get_version(gnutls_x509_crt_t cert)
{
    opaque version[5];
    int len, result;

    if (cert == NULL) {
	gnutls_assert();
	return GNUTLS_E_INVALID_REQUEST;
    }

    len = sizeof(version);
    if ((result =
	 asn1_read_value(cert->cert, "tbsCertificate.version", version,
			 &len)) != ASN1_SUCCESS) {

	if (result == ASN1_ELEMENT_NOT_FOUND)
	    return 1;		/* the DEFAULT version */
	gnutls_assert();
	return _gnutls_asn2err(result);
    }

    return (int) version[0] + 1;
}


static int is_node_printable(ASN1_TYPE x)
{
    switch (type_field(x->type)) {
    case TYPE_TAG:
    case TYPE_SIZE:
    case TYPE_DEFAULT:
	return 0;
    case TYPE_CONSTANT:{
	    ASN1_TYPE up = _asn1_find_up(x);

	    if (up != NULL && type_field(up->type) != TYPE_ANY &&
		up->value != NULL)
		return 0;
	}
	return 1;
    }
    if (x->name == NULL && _asn1_find_up(x) != NULL)
	return 0;
    if (x->value == NULL && x->down == NULL)
	return 0;
    return 1;
}


int decode_private_key_info(const gnutls_datum_t * der,
			    gnutls_x509_privkey_t pkey, ASN1_TYPE * out)
{
    int result, len;
    opaque oid[64], *data = NULL;
    gnutls_datum_t tmp;
    ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY;
    ASN1_TYPE ret_asn;
    int data_size;

    if ((result =
	 asn1_create_element(_gnutls_get_pkix(),
			     "PKIX1.pkcs-8-PrivateKeyInfo",
			     &pkcs8_asn)) != ASN1_SUCCESS) {
	gnutls_assert();
	result = _gnutls_asn2err(result);
	goto error;
    }

    result = asn1_der_decoding(&pkcs8_asn, der->data, der->size, NULL);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	goto error;
    }

    /* Check the private key algorithm OID
     */
    len = sizeof(oid);
    result =
	asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.algorithm",
			oid, &len);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	result = _gnutls_asn2err(result);
	goto error;
    }

    /* we only support RSA private keys.
     */
    if (strcmp(oid, PKIX1_RSA_OID) != 0) {
	gnutls_assert();
	_gnutls_x509_log
	    ("PKCS #8 private key OID '%s' is unsupported.\n", oid);
	result = GNUTLS_E_UNKNOWN_PK_ALGORITHM;
	goto error;
    }

    /* Get the DER encoding of the actual private key.
     */
    data_size = 0;
    result = asn1_read_value(pkcs8_asn, "privateKey", NULL, &data_size);
    if (result != ASN1_MEM_ERROR) {
	gnutls_assert();
	result = _gnutls_asn2err(result);
	goto error;
    }

    data = gnutls_alloca(data_size);
    if (data == NULL) {
	gnutls_assert();
	result = GNUTLS_E_MEMORY_ERROR;
	goto error;
    }

    result = asn1_read_value(pkcs8_asn, "privateKey", data, &data_size);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	result = _gnutls_asn2err(result);
	goto error;
    }

    asn1_delete_structure(&pkcs8_asn);

    tmp.data = data;
    tmp.size = data_size;

    pkey->pk_algorithm = GNUTLS_PK_RSA;

    ret_asn = _gnutls_privkey_decode_pkcs1_rsa_key(&tmp, pkey);
    if (ret_asn == NULL) {
	gnutls_assert();
    }

    *out = ret_asn;

    return 0;

  error:
    asn1_delete_structure(&pkcs8_asn);
    if (data != NULL) {
	gnutls_afree(data);
    }
    return result;

}


static int is_leaf(ASN1_TYPE p)
{
    ASN1_TYPE x;

    if (p == NULL)
	return 1;
    if (p->down == NULL)
	return 1;

    x = p->down;

    while (x != NULL) {
	if (is_node_printable(x))
	    return 0;
	if (is_leaf(x) == 0)
	    return 0;
	x = x->right;
    }

    return 1;

}


_gnutls_asn1_get_structure_xml(ASN1_TYPE structure,
			       gnutls_datum_t * res, int detail)
{
    node_asn *p, *root;
    int k, indent = 0, len, len2, len3;
    opaque tmp[1024];
    char nname[256];
    int ret;
    gnutls_string str;

    if (res == NULL || structure == NULL) {
	gnutls_assert();
	return GNUTLS_E_INVALID_REQUEST;
    }

    _gnutls_string_init(&str, malloc, realloc, free);

    STR_APPEND(XML_HEADER);
    indent = 1;

    root = _asn1_find_node(structure, "");

    if (root == NULL) {
	gnutls_assert();
	_gnutls_string_clear(&str);
	return GNUTLS_E_INTERNAL_ERROR;
    }

    if (detail == GNUTLS_XML_SHOW_ALL)
	ret = asn1_expand_any_defined_by(_gnutls_get_pkix(), &structure);
    /* we don't need to check the error value
     * here.
     */

    if (detail == GNUTLS_XML_SHOW_ALL) {
	ret = _gnutls_x509_expand_extensions(&structure);
	if (ret < 0) {
	    gnutls_assert();
	    return ret;
	}
    }

    p = root;
    while (p) {
	if (is_node_printable(p)) {
	    for (k = 0; k < indent; k++)
		APPEND(" ", 1);

	    if ((ret = normalize_name(p, nname, sizeof(nname))) < 0) {
		_gnutls_string_clear(&str);
		gnutls_assert();
		return ret;
	    }

	    APPEND("<", 1);
	    STR_APPEND(nname);
	}

	if (is_node_printable(p)) {
	    switch (type_field(p->type)) {
	    case TYPE_DEFAULT:
		STR_APPEND(" type=\"DEFAULT\"");
		break;
	    case TYPE_NULL:
		STR_APPEND(" type=\"NULL\"");
		break;
	    case TYPE_IDENTIFIER:
		STR_APPEND(" type=\"IDENTIFIER\"");
		break;
	    case TYPE_INTEGER:
		STR_APPEND(" type=\"INTEGER\"");
		STR_APPEND(" encoding=\"HEX\"");
		break;
	    case TYPE_ENUMERATED:
		STR_APPEND(" type=\"ENUMERATED\"");
		STR_APPEND(" encoding=\"HEX\"");
		break;
	    case TYPE_TIME:
		STR_APPEND(" type=\"TIME\"");
		break;
	    case TYPE_BOOLEAN:
		STR_APPEND(" type=\"BOOLEAN\"");
		break;
	    case TYPE_SEQUENCE:
		STR_APPEND(" type=\"SEQUENCE\"");
		break;
	    case TYPE_BIT_STRING:
		STR_APPEND(" type=\"BIT STRING\"");
		STR_APPEND(" encoding=\"HEX\"");
		break;
	    case TYPE_OCTET_STRING:
		STR_APPEND(" type=\"OCTET STRING\"");
		STR_APPEND(" encoding=\"HEX\"");
		break;
	    case TYPE_SEQUENCE_OF:
		STR_APPEND(" type=\"SEQUENCE OF\"");
		break;
	    case TYPE_OBJECT_ID:
		STR_APPEND(" type=\"OBJECT ID\"");
		break;
	    case TYPE_ANY:
		STR_APPEND(" type=\"ANY\"");
		if (!p->down)
		    STR_APPEND(" encoding=\"HEX\"");
		break;
	    case TYPE_CONSTANT:{
		    ASN1_TYPE up = _asn1_find_up(p);

		    if (up && type_field(up->type) == TYPE_ANY &&
			up->left && up->left->value &&
			up->type & CONST_DEFINED_BY &&
			type_field(up->left->type) == TYPE_OBJECT_ID) {

			if (_gnutls_x509_oid_data_printable
			    (up->left->value) == 0) {
			    STR_APPEND(" encoding=\"HEX\"");
			}

		    }
		}
		break;
	    case TYPE_SET:
		STR_APPEND(" type=\"SET\"");
		break;
	    case TYPE_SET_OF:
		STR_APPEND(" type=\"SET OF\"");
		break;
	    case TYPE_CHOICE:
		STR_APPEND(" type=\"CHOICE\"");
		break;
	    case TYPE_DEFINITIONS:
		STR_APPEND(" type=\"DEFINITIONS\"");
		break;
	    default:
		break;
	    }
	}

	if (p->type == TYPE_BIT_STRING) {
	    len2 = -1;
	    len = _asn1_get_length_der(p->value, &len2);
	    snprintf(tmp, sizeof(tmp), " length=\"%i\"",
		     (len - 1) * 8 - (p->value[len2]));
	    STR_APPEND(tmp);
	}

	if (is_node_printable(p))
	    STR_APPEND(">");

	if (is_node_printable(p)) {
	    const unsigned char *value;

	    if (p->value == NULL)
		value = find_default_value(p);
	    else
		value = p->value;

	    switch (type_field(p->type)) {

	    case TYPE_DEFAULT:
		if (value)
		    STR_APPEND(value);
		break;
	    case TYPE_IDENTIFIER:
		if (value)
		    STR_APPEND(value);
		break;
	    case TYPE_INTEGER:
		if (value) {
		    len2 = -1;
		    len = _asn1_get_length_der(value, &len2);

		    for (k = 0; k < len; k++) {
			snprintf(tmp, sizeof(tmp),
				 "%02X", (value)[k + len2]);
			STR_APPEND(tmp);
		    }

		}
		break;
	    case TYPE_ENUMERATED:
		if (value) {
		    len2 = -1;
		    len = _asn1_get_length_der(value, &len2);

		    for (k = 0; k < len; k++) {
			snprintf(tmp, sizeof(tmp),
				 "%02X", (value)[k + len2]);
			STR_APPEND(tmp);
		    }
		}
		break;
	    case TYPE_TIME:
		if (value)
		    STR_APPEND(value);
		break;
	    case TYPE_BOOLEAN:
		if (value) {
		    if (value[0] == 'T') {
			STR_APPEND("TRUE");
		    } else if (value[0] == 'F') {
			STR_APPEND("FALSE");
		    }
		}
		break;
	    case TYPE_BIT_STRING:
		if (value) {
		    len2 = -1;
		    len = _asn1_get_length_der(value, &len2);

		    for (k = 1; k < len; k++) {
			snprintf(tmp, sizeof(tmp),
				 "%02X", (value)[k + len2]);
			STR_APPEND(tmp);
		    }
		}
		break;
	    case TYPE_OCTET_STRING:
		if (value) {
		    len2 = -1;
		    len = _asn1_get_length_der(value, &len2);
		    for (k = 0; k < len; k++) {
			snprintf(tmp, sizeof(tmp),
				 "%02X", (value)[k + len2]);
			STR_APPEND(tmp);
		    }
		}
		break;
	    case TYPE_OBJECT_ID:
		if (value)
		    STR_APPEND(value);
		break;
	    case TYPE_ANY:
		if (!p->down) {
		    if (value) {
			len3 = -1;
			len2 = _asn1_get_length_der(value, &len3);
			for (k = 0; k < len2; k++) {
			    snprintf(tmp, sizeof(tmp),
				     "%02X", (value)[k + len3]);
			    STR_APPEND(tmp);
			}
		    }
		}
		break;
	    case TYPE_CONSTANT:{
		    ASN1_TYPE up = _asn1_find_up(p);

		    if (up && type_field(up->type) == TYPE_ANY &&
			up->left && up->left->value &&
			up->type & CONST_DEFINED_BY &&
			type_field(up->left->type) == TYPE_OBJECT_ID) {

			len2 = _asn1_get_length_der(up->value, &len3);

			if (len2 > 0 && strcmp(p->name, "type") == 0) {
			    size_t len = sizeof(tmp);
			    ret =
				_gnutls_x509_oid_data2string(up->left->
							     value,
							     up->value +
							     len3, len2,
							     tmp, &len);

			    if (ret >= 0) {
				STR_APPEND(tmp);
			    }
			} else {
			    for (k = 0; k < len2; k++) {
				snprintf(tmp, sizeof(tmp),
					 "%02X", (up->value)[k + len3]);
				STR_APPEND(tmp);
			    }

			}
		    } else {
			if (value)
			    STR_APPEND(value);
		    }

		}
		break;
	    case TYPE_SET:
	    case TYPE_SET_OF:
	    case TYPE_CHOICE:
	    case TYPE_DEFINITIONS:
	    case TYPE_SEQUENCE_OF:
	    case TYPE_SEQUENCE:
	    case TYPE_NULL:
		break;
	    default:
		break;
	    }
	}

	if (p->down && is_node_printable(p)) {
	    ASN1_TYPE x;
	    p = p->down;
	    indent += 2;
	    x = p;
	    do {
		if (is_node_printable(x)) {
		    STR_APPEND("\n");
		    break;
		}
		x = x->right;
	    } while (x != NULL);
	} else if (p == root) {
	    if (is_node_printable(p)) {
		if ((ret = normalize_name(p, nname, sizeof(nname))) < 0) {
		    _gnutls_string_clear(&str);
		    gnutls_assert();
		    return ret;
		}

		APPEND("</", 2);
		STR_APPEND(nname);
		APPEND(">\n", 2);
	    }
	    p = NULL;
	    break;
	} else {
	    if (is_node_printable(p)) {
		if ((ret = normalize_name(p, nname, sizeof(nname))) < 0) {
		    _gnutls_string_clear(&str);
		    gnutls_assert();
		    return ret;
		}

		APPEND("</", 2);
		STR_APPEND(nname);
		APPEND(">\n", 2);
	    }
	    if (p->right)
		p = p->right;
	    else {
		while (1) {
		    ASN1_TYPE old_p;

		    old_p = p;

		    p = _asn1_find_up(p);
		    indent -= 2;
		    if (is_node_printable(p)) {
			if (!is_leaf(p))	/* XXX */
			    for (k = 0; k < indent; k++)
				STR_APPEND(" ");

			if ((ret =
			     normalize_name(p, nname,
					    sizeof(nname))) < 0) {
			    _gnutls_string_clear(&str);
			    gnutls_assert();
			    return ret;
			}

			APPEND("</", 2);
			STR_APPEND(nname);
			APPEND(">\n", 2);
		    }
		    if (p == root) {
			p = NULL;
			break;
		    }

		    if (p->right) {
			p = p->right;
			break;
		    }
		}
	    }
	}
    }

    STR_APPEND(XML_FOOTER);
    APPEND("\n\0", 2);

    *res = _gnutls_string2datum(&str);
    res->size -= 1;		/* null is not included in size */

    return 0;
}


static void lzx_reset_state(struct lzx_stream *lzx) {
  int i;

  lzx->R0              = 1;
  lzx->R1              = 1;
  lzx->R2              = 1;
  lzx->header_read     = 0;
  lzx->block_remaining = 0;
  lzx->block_type      = LZX_BLOCKTYPE_INVALID;

  /* initialise tables to 0 (because deltas will be applied to them) */
  for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) lzx->MAINTREE_len[i] = 0;
  for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   lzx->LENGTH_len[i]   = 0;
}


static long keyring_read(const struct key *keyring,
			 char __user *buffer, size_t buflen)
{
	struct keyring_list *klist;
	struct key *key;
	size_t qty, tmp;
	int loop, ret;

	ret = 0;
	klist = keyring->payload.subscriptions;

	if (klist) {
		/* calculate how much data we could return */
		qty = klist->nkeys * sizeof(key_serial_t);

		if (buffer && buflen > 0) {
			if (buflen > qty)
				buflen = qty;

			/* copy the IDs of the subscribed keys into the
			 * buffer */
			ret = -EFAULT;

			for (loop = 0; loop < klist->nkeys; loop++) {
				key = klist->keys[loop];

				tmp = sizeof(key_serial_t);
				if (tmp > buflen)
					tmp = buflen;

				if (copy_to_user(buffer,
						 &key->serial,
						 tmp) != 0)
					goto error;

				buflen -= tmp;
				if (buflen == 0)
					break;
				buffer += tmp;
			}
		}

		ret = qty;
	}

 error:
	return ret;

} /* end keyring_read() */


int key_link(struct key *keyring, struct key *key)
{
	int ret;

	key_check(keyring);
	key_check(key);

	down_write(&keyring->sem);
	ret = __key_link(keyring, key);
	up_write(&keyring->sem);

	return ret;

} /* end key_link() */


static void keyring_publish_name(struct key *keyring)
{
	int bucket;

	if (keyring->description) {
		bucket = keyring_hash(keyring->description);

		write_lock(&keyring_name_lock);

		if (!keyring_name_hash[bucket].next)
			INIT_LIST_HEAD(&keyring_name_hash[bucket]);

		list_add_tail(&keyring->type_data.link,
			      &keyring_name_hash[bucket]);

		write_unlock(&keyring_name_lock);
	}

} /* end keyring_publish_name() */