diff -u modules/comment/comment.admin.inc modules/comment_work/comment.admin.inc
--- modules/comment/comment.admin.inc	2008-05-19 09:27:35.000000000 +0200
+++ modules/comment_work/comment.admin.inc	2009-06-30 11:57:25.000000000 +0200
@@ -64,7 +64,7 @@
   while ($comment = db_fetch_object($result)) {
     $comments[$comment->cid] = '';
     $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
-    $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-'. $comment->cid)));
+    $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'comment/perm/'. $comment->cid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-'. $comment->cid)));
     $form['username'][$comment->cid] = array('#value' => theme('username', $comment));
     $form['node_title'][$comment->cid] = array('#value' => l($comment->node_title, 'node/'. $comment->nid));
     $form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small'));
diff -u modules/comment/comment.module modules/comment_work/comment.module
--- modules/comment/comment.module	2009-05-13 19:15:10.000000000 +0200
+++ modules/comment_work/comment.module	2009-06-30 12:58:47.000000000 +0200
@@ -224,6 +224,14 @@
     'file' => 'comment.admin.inc',
   );
 
+  $items['comment/perm'] = array(
+    'title' => 'Comment permalink',
+    'page callback' => 'comment_permalink',
+    'access arguments' => array('access comments'),
+    'type' => MENU_CALLBACK,
+    'file' => 'comment.module',
+  );
+
   $items['comment/edit'] = array(
     'title' => 'Edit comment',
     'page callback' => 'comment_edit',
@@ -244,6 +252,49 @@
   return $items;
 }
 
+/*
+ * Redirects comment links to the correct page depending on comment settings.
+ *
+ * Since comments are paged there is no way to guarantee which page a comment
+ * appears on. Comment paging and threading settings may be changed at any time.
+ * With threaded comments, an individual comment may move between pages as
+ * comments can be added either before or after it in the overall discussion.
+ * Therefore we use a central routing function for comment links, which
+ * calculates the page number based on current comment settings and returns
+ * the full comment view with the pager set dynamically.
+ *
+ * @param $comment
+ *   A comment object.
+ * @return
+ *   The comment listing set to the page on which the comment appears.
+ */
+ 
+function comment_permalink($cid) {
+
+  settype($cid, "integer");
+  $comment = db_fetch_object(db_query('SELECT c.cid, c.nid FROM {comments} c WHERE c.cid = %d', $cid));
+  $comment = drupal_unpack($comment); 
+  
+  $node = node_load($comment->nid);
+  if ($node && $comment) {
+
+    // Find the current display page for this comment.
+    $page = comment_get_display_page($comment->cid, $node);
+    // Set $_GET['q'] and $_GET['page'] ourselves so that the node callback
+    // behaves as it would when visiting the page directly.
+    $_GET['q'] = 'node/' . $node->nid;
+    $_GET['page'] = $page;
+
+    // Set the node path as the canonical URL to prevent duplicate content.
+    drupal_add_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid)));
+
+    // Return the node view, this will show the correct comment in context.
+    return menu_execute_active_handler('node/' . $node->nid);
+  }
+  drupal_not_found();
+}
+
+
 /**
  * Implementation of hook_node_type().
  */
@@ -393,7 +444,7 @@
 function theme_comment_block() {
   $items = array();
   foreach (comment_get_recent() as $comment) {
-    $items[] = l($comment->subject, 'node/'. $comment->nid, array('fragment' => 'comment-'. $comment->cid)) .'<br />'. t('@time ago', array('@time' => format_interval(time() - $comment->timestamp)));
+    $items[] = l($comment->subject, 'comment/perm/'. $comment->cid, array('fragment' => 'comment-'. $comment->cid)) .'<br />'. t('@time ago', array('@time' => format_interval(time() - $comment->timestamp)));
   }
   if ($items) {
     return theme('item_list', $items);
@@ -976,7 +1027,7 @@
       }
       else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
         if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
-          $query .= ' ORDER BY c.cid';
+          $query .= ' ORDER BY c.cid ASC';
         }
         else {
           // See comment above. Analysis reveals that this doesn't cost too
@@ -1059,6 +1110,89 @@
   return $output;
 }
 
+
+ /**
+ * Get the display ordinal for a comment, starting from 0.
+ *
+ * Count the number of comments which appear before the comment we want to
+ * display, taking into account display settings and threading.
+ *
+ * @param $cid
+ *   The comment ID.
+ * @param $node_type
+ *   The node type of the comment's parent.
+ * @return
+ *   The display ordinal for the comment.
+ * @see comment_get_display_page()
+ */
+function comment_get_display_ordinal($cid, $node) {
+  // Count how many comments (c1) are before $cid (c2) in display order. This is
+  // the 0-based display ordinal.
+  
+  // basic query skeleton...
+  
+  $query = 'SELECT COUNT(*) as count FROM {comments} c1 INNER JOIN {comments} c2 ON c2.nid = c1.nid where c2.cid = %d ';
+  
+  // exclude unpublished comments for users w/o the required user rights.
+  
+  if (!user_access('administer comments')) {
+    $query .= 'AND c1.status = %d ';
+  }
+  
+  $mode = _comment_get_display_setting('mode', $node);
+  $mode_sort = _comment_get_display_setting('sort', $node);
+  
+  if ($mode == COMMENT_MODE_FLAT_EXPANDED || $mode == COMMENT_MODE_FLAT_COLLAPSED) {
+    // For flat comments, cid is used for ordering comments due to
+    // unpredicatable behavior with timestamp, so we make the same assumption
+    // here.
+    
+    
+    if($mode_sort == COMMENT_ORDER_OLDEST_FIRST) {
+        $query .= 'AND c1.cid < c2.cid ';
+    } 
+    else {
+	$query .= 'AND c1.cid > c2.cid ';
+    }
+  }
+  else {
+    // For threaded comments, the c.thread column is used for ordering. We can
+    // use the vancode for comparison, but must remove the trailing slash.
+    // @see comment_render().
+    // $query->where('SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))');
+
+    if($mode_sort == COMMENT_ORDER_OLDEST_FIRST) {
+	$query .= 'AND SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))';
+    }
+    else {
+	$query .= 'AND SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) > SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))';
+    }
+  }
+  $count = db_fetch_object(db_query($query, $cid, COMMENT_PUBLISHED));
+  $count = drupal_unpack($count);
+  return $count->count;
+}
+
+/**
+ * Return the page number for a comment.
+ *
+ * Finds the correct page number for a comment taking into account display
+ * and paging settings.
+ *
+ * @param $cid
+ *   The comment ID.
+ * @param $node_type
+ *   The node type the comment is attached to.
+ * @return
+ *   The page number.
+ */
+function comment_get_display_page($cid, $node) {
+  $ordinal = comment_get_display_ordinal($cid, $node);
+  $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+  return floor($ordinal / $comments_per_page);
+}
+
+
 /**
  * Comment operations. We offer different update operations depending on
  * which comment administration page we're on.
@@ -1540,11 +1674,7 @@
 function comment_form_submit($form, &$form_state) {
   _comment_form_submit($form_state['values']);
   if ($cid = comment_save($form_state['values'])) {
-    $node = node_load($form_state['values']['nid']);
-    // Add 1 to existing $node->comment count to include new comment being added.
-    $comment_count = $node->comment_count + 1;
-    $page = comment_new_page_count($comment_count, 1, $node);
-    $form_state['redirect'] = array('node/'. $node->nid, $page, "comment-$cid");
+    $form_state['redirect'] = array('comment/perm/' . $cid, array(), "comment-$cid");
     return;
   }
 }
@@ -1691,7 +1821,7 @@
   $variables['picture']   = theme_get_setting('toggle_comment_user_picture') ? theme('user_picture', $comment) : '';
   $variables['signature'] = $comment->signature;
   $variables['submitted'] = theme('comment_submitted', $comment);
-  $variables['title']     = l($comment->subject, $_GET['q'], array('fragment' => "comment-$comment->cid"));
+  $variables['title']     = l($comment->subject, 'comment/perm/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
   $variables['template_files'][] = 'comment-'. $node->type;
   // set status to a string representation of comment->status.
   if (isset($comment->preview)) {
@@ -1713,7 +1843,7 @@
   $variables['author'] = theme('username', $comment);
   $variables['date']   = format_date($comment->timestamp);
   $variables['new']    = $comment->new ? t('new') : '';
-  $variables['title']  = l($comment->subject, comment_node_url() .'/'. $comment->cid, array('fragment' => "comment-$comment->cid"));
+  $variables['title']  = l($comment->subject, 'comment/perm/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
 }
 
 /**
